Skip to content

Commit 65ea6f9

Browse files
committed
adds authorization to the scheduled publishing , this new attribute can be used to secure scheduled tasks as well.
1 parent 9158ea1 commit 65ea6f9

File tree

5 files changed

+117
-4
lines changed

5 files changed

+117
-4
lines changed
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
using System;
2+
using System.Text;
3+
using System.Text.RegularExpressions;
4+
using System.Web;
5+
using System.Web.Mvc;
6+
using Umbraco.Core;
7+
using Umbraco.Core.Logging;
8+
9+
namespace Umbraco.Web.Mvc
10+
{
11+
/// <summary>
12+
/// Used for authorizing scheduled tasks
13+
/// </summary>
14+
public sealed class AdminTokenAuthorizeAttribute : AuthorizeAttribute
15+
{
16+
private readonly ApplicationContext _applicationContext;
17+
18+
/// <summary>
19+
/// THIS SHOULD BE ONLY USED FOR UNIT TESTS
20+
/// </summary>
21+
/// <param name="appContext"></param>
22+
public AdminTokenAuthorizeAttribute(ApplicationContext appContext)
23+
{
24+
if (appContext == null) throw new ArgumentNullException("appContext");
25+
_applicationContext = appContext;
26+
}
27+
28+
public AdminTokenAuthorizeAttribute()
29+
{
30+
}
31+
32+
private ApplicationContext GetApplicationContext()
33+
{
34+
return _applicationContext ?? ApplicationContext.Current;
35+
}
36+
37+
/// <summary>
38+
/// Used to return the value that needs to go in the Authorization header
39+
/// </summary>
40+
/// <param name="appContext"></param>
41+
/// <returns></returns>
42+
public static string GetAuthHeaderTokenVal(ApplicationContext appContext)
43+
{
44+
var admin = appContext.Services.UserService.GetUserById(0);
45+
46+
var token = string.Format("{0}u____u{1}u____u{2}", admin.Email, admin.Username, admin.RawPasswordValue);
47+
48+
var encrypted = token.EncryptWithMachineKey();
49+
var bytes = Encoding.UTF8.GetBytes(encrypted);
50+
var base64 = Convert.ToBase64String(bytes);
51+
return "AToken val=\"" + base64 + "\"";
52+
}
53+
54+
/// <summary>
55+
/// Ensures that the user must be in the Administrator or the Install role
56+
/// </summary>
57+
/// <param name="httpContext"></param>
58+
/// <returns></returns>
59+
protected override bool AuthorizeCore(HttpContextBase httpContext)
60+
{
61+
if (httpContext == null) throw new ArgumentNullException("httpContext");
62+
63+
var appContext = GetApplicationContext();
64+
65+
//we need to that the app is configured and that a user is logged in
66+
if (appContext.IsConfigured == false) return false;
67+
68+
//need the auth header
69+
if (httpContext.Request.Headers["Authorization"] == null || httpContext.Request.Headers["Authorization"].IsNullOrWhiteSpace()) return false;
70+
71+
var header = httpContext.Request.Headers["Authorization"];
72+
if (header.StartsWith("AToken ") == false) return false;
73+
74+
var keyVal = Regex.Matches(header, "AToken val=(.*?)(?:$|\\s)");
75+
if (keyVal.Count != 1) return false;
76+
if (keyVal[0].Groups.Count != 2) return false;
77+
78+
var admin = appContext.Services.UserService.GetUserById(0);
79+
if (admin == null) return false;
80+
81+
try
82+
{
83+
//get the token value from the header
84+
var val = keyVal[0].Groups[1].Value.Trim("\"");
85+
//un-base 64 the string
86+
var bytes = Convert.FromBase64String(val);
87+
var encrypted = Encoding.UTF8.GetString(bytes);
88+
//decrypt the string
89+
var text = encrypted.DecryptWithMachineKey();
90+
91+
//split
92+
var split = text.Split(new[] {"u____u"}, StringSplitOptions.RemoveEmptyEntries);
93+
if (split.Length != 3) return false;
94+
95+
//compare
96+
return
97+
split[0] == admin.Email
98+
&& split[1] == admin.Username
99+
&& split[2] == admin.RawPasswordValue;
100+
}
101+
catch (Exception ex)
102+
{
103+
LogHelper.Error<AdminTokenAuthorizeAttribute>("Failed to format passed in token value", ex);
104+
return false;
105+
}
106+
}
107+
}
108+
}

src/Umbraco.Web/Mvc/UmbracoAuthorizeAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
namespace Umbraco.Web.Mvc
99
{
10-
/// <summary>
10+
/// <summary>
1111
/// Ensures authorization is successful for a back office user
1212
/// </summary>
1313
public sealed class UmbracoAuthorizeAttribute : AuthorizeAttribute

src/Umbraco.Web/Scheduling/ScheduledPublishing.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using System;
22
using System.Diagnostics;
33
using System.Net;
4+
using System.Text;
45
using Umbraco.Core;
56
using Umbraco.Core.Logging;
67
using Umbraco.Core.Publishing;
78
using Umbraco.Core.Sync;
9+
using Umbraco.Web.Mvc;
810

911
namespace Umbraco.Web.Scheduling
1012
{
@@ -28,7 +30,10 @@ public void Start(object sender)
2830
var umbracoBaseUrl = ServerEnvironmentHelper.GetCurrentServerUmbracoBaseUrl();
2931
var url = string.Format("{0}/RestServices/ScheduledPublish/", umbracoBaseUrl);
3032
using (var wc = new WebClient())
31-
{
33+
{
34+
//pass custom the authorization header
35+
wc.Headers.Set("Authorization", AdminTokenAuthorizeAttribute.GetAuthHeaderTokenVal(appContext));
36+
3237
var result = wc.UploadString(url, "");
3338
}
3439
}

src/Umbraco.Web/Umbraco.Web.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@
302302
<Compile Include="Models\IRenderModel.cs" />
303303
<Compile Include="Models\PostRedirectModel.cs" />
304304
<Compile Include="Models\PublishedProperty.cs" />
305+
<Compile Include="Mvc\AdminTokenAuthorizeAttribute.cs" />
305306
<Compile Include="Mvc\RedirectToUmbracoUrlResult.cs" />
306307
<Compile Include="PublishedCache\MemberPublishedContent.cs" />
307308
<Compile Include="PublishedCache\RawValueProperty.cs" />

src/Umbraco.Web/WebServices/ScheduledPublishController.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@
77

88
namespace Umbraco.Web.WebServices
99
{
10-
//TODO: How to authenticate?
11-
1210
/// <summary>
1311
/// A REST controller used for running the scheduled publishing, this is called from the background worker timer
1412
/// </summary>
13+
[AdminTokenAuthorize]
1514
public class ScheduledPublishController : UmbracoController
1615
{
1716
private static bool _isPublishingRunning = false;

0 commit comments

Comments
 (0)