Working on user timeouts - now have the user timeout time being nicely tracked in the back office with a bit of injector magic both on the client side and the server side with filters. Now to wire up the call to get remaining seconds if a request hasn't been made for a specified amount of time, then we can add UI notification about timeout period.
This commit is contained in:
@@ -38,19 +38,17 @@ namespace Umbraco.Core.Security
|
||||
/// Renews the Umbraco authentication ticket
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <param name="timeoutInMinutes"></param>
|
||||
/// <returns></returns>
|
||||
public static bool RenewUmbracoAuthTicket(this HttpContextBase http, int timeoutInMinutes = 60)
|
||||
public static bool RenewUmbracoAuthTicket(this HttpContextBase http)
|
||||
{
|
||||
return RenewAuthTicket(http,
|
||||
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieName,
|
||||
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain,
|
||||
timeoutInMinutes);
|
||||
UmbracoConfig.For.UmbracoSettings().Security.AuthCookieDomain);
|
||||
}
|
||||
|
||||
internal static bool RenewUmbracoAuthTicket(this HttpContext http, int timeoutInMinutes = 60)
|
||||
internal static bool RenewUmbracoAuthTicket(this HttpContext http)
|
||||
{
|
||||
return new HttpContextWrapper(http).RenewUmbracoAuthTicket(timeoutInMinutes);
|
||||
return new HttpContextWrapper(http).RenewUmbracoAuthTicket();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -65,6 +63,7 @@ namespace Umbraco.Core.Security
|
||||
http,
|
||||
userdata.Username,
|
||||
userDataString,
|
||||
//use the configuration timeout - this is the same timeout that will be used when renewing the ticket.
|
||||
GlobalSettings.TimeOutInMinutes,
|
||||
//Umbraco has always persisted it's original cookie for 1 day so we'll keep it that way
|
||||
1440,
|
||||
@@ -78,6 +77,23 @@ namespace Umbraco.Core.Security
|
||||
new HttpContextWrapper(http).CreateUmbracoAuthTicket(userdata);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// returns the number of seconds the user has until their auth session times out
|
||||
/// </summary>
|
||||
/// <param name="http"></param>
|
||||
/// <returns></returns>
|
||||
public static double GetRemainingAuthSeconds(this HttpContextBase http)
|
||||
{
|
||||
var ticket = http.GetUmbracoAuthTicket();
|
||||
if (ticket == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
var utcExpired = ticket.Expiration.ToUniversalTime();
|
||||
var secondsRemaining = utcExpired.Subtract(DateTime.UtcNow).TotalSeconds;
|
||||
return secondsRemaining;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the umbraco auth ticket
|
||||
/// </summary>
|
||||
@@ -143,9 +159,8 @@ namespace Umbraco.Core.Security
|
||||
/// <param name="http"></param>
|
||||
/// <param name="cookieName"></param>
|
||||
/// <param name="cookieDomain"></param>
|
||||
/// <param name="minutesPersisted"></param>
|
||||
/// <returns></returns>
|
||||
private static bool RenewAuthTicket(this HttpContextBase http, string cookieName, string cookieDomain, int minutesPersisted)
|
||||
/// <returns>true if there was a ticket to renew otherwise false if there was no ticket</returns>
|
||||
private static bool RenewAuthTicket(this HttpContextBase http, string cookieName, string cookieDomain)
|
||||
{
|
||||
//get the ticket
|
||||
var ticket = GetAuthTicket(http, cookieName);
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
|
||||
|
||||
@@ -55,6 +55,17 @@ function authResource($q, $http, umbRequestHelper, angularHelper) {
|
||||
"IsAuthenticated")),
|
||||
'Server call failed for checking authentication');
|
||||
},
|
||||
|
||||
/** Gets the user's remaining seconds before their login times out */
|
||||
getRemainingTimeoutSeconds: function () {
|
||||
|
||||
return umbRequestHelper.resourcePromise(
|
||||
$http.get(
|
||||
umbRequestHelper.getApiUrl(
|
||||
"authenticationApiBaseUrl",
|
||||
"GetRemainingTimeoutSeconds")),
|
||||
'Server call failed for checking remaining seconds');
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,45 +1,63 @@
|
||||
angular.module('umbraco.security.interceptor', ['umbraco.security.retryQueue'])
|
||||
// This http interceptor listens for authentication successes and failures
|
||||
.factory('securityInterceptor', ['$injector', 'securityRetryQueue', 'notificationsService', function ($injector, queue, notifications) {
|
||||
return function(promise) {
|
||||
|
||||
// This http interceptor listens for authentication failures
|
||||
.factory('securityInterceptor', ['$injector', 'securityRetryQueue', 'notificationsService', function ($injector, queue, notifications) {
|
||||
return function (promise) {
|
||||
// Intercept failed requests
|
||||
return promise.then(null, function (originalResponse) {
|
||||
|
||||
//A 401 means that the user is not logged in
|
||||
if (originalResponse.status === 401) {
|
||||
return promise.then(
|
||||
function(originalResponse) {
|
||||
// Intercept successful requests
|
||||
|
||||
//Here we'll check if our custom header is in the response which indicates how many seconds the user's session has before it
|
||||
//expires. Then we'll update the user in the user service accordingly.
|
||||
var headers = originalResponse.headers();
|
||||
if (headers["x-umb-user-seconds"]) {
|
||||
var asNumber = parseFloat(headers["x-umb-user-seconds"]);
|
||||
if (!isNaN(asNumber)) {
|
||||
// We must use $injector to get the $http service to prevent circular dependency
|
||||
var userService = $injector.get('userService');
|
||||
userService.setUserTimeout(asNumber);
|
||||
}
|
||||
}
|
||||
|
||||
return promise;
|
||||
}, function(originalResponse) {
|
||||
// Intercept failed requests
|
||||
|
||||
// The request bounced because it was not authorized - add a new request to the retry queue
|
||||
promise = queue.pushRetryFn('unauthorized-server', function retryRequest() {
|
||||
// We must use $injector to get the $http service to prevent circular dependency
|
||||
return $injector.get('$http')(originalResponse.config);
|
||||
//A 401 means that the user is not logged in
|
||||
if (originalResponse.status === 401) {
|
||||
|
||||
// The request bounced because it was not authorized - add a new request to the retry queue
|
||||
promise = queue.pushRetryFn('unauthorized-server', function retryRequest() {
|
||||
// We must use $injector to get the $http service to prevent circular dependency
|
||||
return $injector.get('$http')(originalResponse.config);
|
||||
});
|
||||
}
|
||||
else if (originalResponse.status === 403) {
|
||||
//if the status was a 403 it means the user didn't have permission to do what the request was trying to do.
|
||||
//How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was
|
||||
//requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper,
|
||||
// or completely custom for services calling resources.
|
||||
|
||||
//http://issues.umbraco.org/issue/U4-2749
|
||||
|
||||
//It was decided to just put these messages into the normal status messages.
|
||||
|
||||
var msg = "Unauthorized access to URL: <br/><i>" + originalResponse.config.url.split('?')[0] + "</i>";
|
||||
if (originalResponse.config.data) {
|
||||
msg += "<br/> with data: <br/><i>" + angular.toJson(originalResponse.config.data) + "</i><br/>Contact your administrator for information.";
|
||||
}
|
||||
|
||||
notifications.error(
|
||||
"Authorization error",
|
||||
msg);
|
||||
}
|
||||
|
||||
return promise;
|
||||
});
|
||||
}
|
||||
else if (originalResponse.status === 403) {
|
||||
//if the status was a 403 it means the user didn't have permission to do what the request was trying to do.
|
||||
//How do we deal with this now, need to tell the user somehow that they don't have permission to do the thing that was
|
||||
//requested. We can either deal with this globally here, or we can deal with it globally for individual requests on the umbRequestHelper,
|
||||
// or completely custom for services calling resources.
|
||||
|
||||
//http://issues.umbraco.org/issue/U4-2749
|
||||
|
||||
//It was decided to just put these messages into the normal status messages.
|
||||
};
|
||||
}])
|
||||
|
||||
var msg = "Unauthorized access to URL: <br/><i>" + originalResponse.config.url.split('?')[0] + "</i>";
|
||||
if (originalResponse.config.data) {
|
||||
msg += "<br/> with data: <br/><i>" + angular.toJson(originalResponse.config.data) + "</i><br/>Contact your administrator for information.";
|
||||
}
|
||||
|
||||
notifications.error(
|
||||
"Authorization error",
|
||||
msg);
|
||||
}
|
||||
return promise;
|
||||
});
|
||||
};
|
||||
}])
|
||||
|
||||
// We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block.
|
||||
.config(['$httpProvider', function ($httpProvider) {
|
||||
$httpProvider.responseInterceptors.push('securityInterceptor');
|
||||
}]);
|
||||
// We have to add the interceptor to the queue as a string because the interceptor depends upon service instances that are not available in the config block.
|
||||
.config(['$httpProvider', function ($httpProvider) {
|
||||
$httpProvider.responseInterceptors.push('securityInterceptor');
|
||||
}]);
|
||||
@@ -1,5 +1,5 @@
|
||||
angular.module('umbraco.services')
|
||||
.factory('userService', function ($rootScope, $q, $location, $log, securityRetryQueue, authResource, dialogService) {
|
||||
.factory('userService', function ($rootScope, $q, $location, $log, securityRetryQueue, authResource, dialogService, $timeout) {
|
||||
|
||||
var currentUser = null;
|
||||
var lastUserId = null;
|
||||
@@ -34,6 +34,31 @@ angular.module('umbraco.services')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
This methods will set the current user when it is resolved and
|
||||
will then start the counter to count in-memory how many seconds they have
|
||||
remaining on the auth session
|
||||
*/
|
||||
function setCurrentUser(usr) {
|
||||
if (!usr.remainingAuthSeconds) {
|
||||
throw "The user object is invalid, the remainingAuthSeconds is required.";
|
||||
}
|
||||
currentUser = usr;
|
||||
//start the timer
|
||||
setCurrentUserTimeout(usr);
|
||||
}
|
||||
function setCurrentUserTimeout() {
|
||||
$timeout(function () {
|
||||
if (currentUser) {
|
||||
currentUser.remainingAuthSeconds -= 1;
|
||||
if (currentUser.remainingAuthSeconds > 0) {
|
||||
//recurse!
|
||||
setCurrentUserTimeout();
|
||||
}
|
||||
}
|
||||
}, 1000);//every second
|
||||
}
|
||||
|
||||
// Register a handler for when an item is added to the retry queue
|
||||
securityRetryQueue.onItemAddedCallbacks.push(function (retryItem) {
|
||||
if (securityRetryQueue.hasMore()) {
|
||||
@@ -76,7 +101,7 @@ angular.module('umbraco.services')
|
||||
.then(function (data) {
|
||||
|
||||
//when it's successful, return the user data
|
||||
currentUser = data;
|
||||
setCurrentUser(data);
|
||||
|
||||
var result = { user: data, authenticated: true, lastUserId: lastUserId };
|
||||
|
||||
@@ -119,7 +144,7 @@ angular.module('umbraco.services')
|
||||
$rootScope.$broadcast("authenticated", result);
|
||||
}
|
||||
|
||||
currentUser = data;
|
||||
setCurrentUser(data);
|
||||
currentUser.avatar = 'http://www.gravatar.com/avatar/' + data.emailHash + '?s=40&d=404';
|
||||
deferred.resolve(currentUser);
|
||||
});
|
||||
@@ -130,6 +155,12 @@ angular.module('umbraco.services')
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
setUserTimeout: function(newTimeout) {
|
||||
if (currentUser && angular.isNumber(newTimeout)) {
|
||||
currentUser.remainingAuthSeconds = newTimeout;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -69,7 +69,9 @@ h1.headline{height: 20px; padding: 30px 0 0 20px;}
|
||||
width: 100%;
|
||||
margin: -2px 0 0 0;
|
||||
}
|
||||
|
||||
.umb-panel-header p {
|
||||
margin:0px 20px;
|
||||
}
|
||||
.umb-headline-editor-wrapper input {
|
||||
background: none;
|
||||
border: none;
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
<h1 class="headline">{{user.name}}</h1>
|
||||
|
||||
<p class="muted">
|
||||
<small>Session expires in {{user.remainingAuthSeconds | number:0}} seconds</small>
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="umb-panel-body umb-scrollable">
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using AutoMapper;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Web.Models.ContentEditing;
|
||||
using Umbraco.Web.Models.Mapping;
|
||||
using Umbraco.Web.Mvc;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Security;
|
||||
using Umbraco.Web.WebApi;
|
||||
using Umbraco.Web.WebApi.Filters;
|
||||
@@ -31,6 +34,25 @@ namespace Umbraco.Web.Editors
|
||||
base.Initialize(controllerContext);
|
||||
controllerContext.Configuration.Formatters.Remove(controllerContext.Configuration.Formatters.XmlFormatter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a special method that will return the current users' remaining session seconds, the reason
|
||||
/// it is special is because this route is ignored in the UmbracoModule so that the auth ticket doesn't get
|
||||
/// updated with a new timeout.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[WebApi.UmbracoAuthorize]
|
||||
public double GetRemainingTimeoutSeconds()
|
||||
{
|
||||
var httpContextAttempt = TryGetHttpContext();
|
||||
if (httpContextAttempt.Success)
|
||||
{
|
||||
return httpContextAttempt.Result.GetRemainingAuthSeconds();
|
||||
}
|
||||
|
||||
//we need an http context
|
||||
throw new NotSupportedException("An HttpContext is required for this request");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current user's cookie is valid and if so returns OK or a 400 (BadRequest)
|
||||
@@ -53,23 +75,23 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the current user's cookie is valid and if so returns the user object associated
|
||||
/// Returns the currently logged in Umbraco user
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[WebApi.UmbracoAuthorize]
|
||||
public UserDetail GetCurrentUser()
|
||||
{
|
||||
var attempt = UmbracoContext.Security.AuthorizeRequest();
|
||||
if (attempt == ValidateRequestAttempt.Success)
|
||||
var user = Services.UserService.GetUserById(UmbracoContext.Security.GetUserId());
|
||||
var result = Mapper.Map<UserDetail>(user);
|
||||
var httpContextAttempt = TryGetHttpContext();
|
||||
if (httpContextAttempt.Success)
|
||||
{
|
||||
var user = Services.UserService.GetUserById(UmbracoContext.Security.GetUserId());
|
||||
return Mapper.Map<UserDetail>(user);
|
||||
//set their remaining seconds
|
||||
result.SecondsUntilTimeout = httpContextAttempt.Result.GetRemainingAuthSeconds();
|
||||
}
|
||||
|
||||
//return Unauthorized (401) because the user is not authorized right now
|
||||
throw new HttpResponseException(HttpStatusCode.Unauthorized);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Logs a user in
|
||||
/// </summary>
|
||||
@@ -83,7 +105,14 @@ namespace Umbraco.Web.Editors
|
||||
var user = Services.UserService.GetUserByUserName(username);
|
||||
//TODO: Clean up the int cast!
|
||||
UmbracoContext.Security.PerformLogin((int)user.Id);
|
||||
return Mapper.Map<UserDetail>(user);
|
||||
var result = Mapper.Map<UserDetail>(user);
|
||||
var httpContextAttempt = TryGetHttpContext();
|
||||
if (httpContextAttempt.Success)
|
||||
{
|
||||
//set their remaining seconds
|
||||
result.SecondsUntilTimeout = httpContextAttempt.Result.GetRemainingAuthSeconds();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//return BadRequest (400), we don't want to return a 401 because that get's intercepted
|
||||
|
||||
@@ -19,5 +19,11 @@ namespace Umbraco.Web.Models.ContentEditing
|
||||
/// </summary>
|
||||
[DataMember(Name = "emailHash")]
|
||||
public string EmailHash { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets/sets the number of seconds for the user's auth ticket to expire
|
||||
/// </summary>
|
||||
[DataMember(Name = "remainingAuthSeconds")]
|
||||
public double SecondsUntilTimeout { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@ namespace Umbraco.Web.Models.Mapping
|
||||
.ForMember(
|
||||
detail => detail.EmailHash,
|
||||
opt => opt.MapFrom(user => user.Email.ToLowerInvariant().Trim().ToMd5()));
|
||||
|
||||
config.CreateMap<IProfile, UserBasic>()
|
||||
.ForMember(detail => detail.UserId, opt => opt.MapFrom(profile => GetIntId(profile.Id)));
|
||||
}
|
||||
|
||||
@@ -166,7 +166,7 @@ namespace Umbraco.Web.Security
|
||||
|
||||
public void RenewLoginTimeout()
|
||||
{
|
||||
_httpContext.RenewUmbracoAuthTicket(UmbracoTimeOutInMinutes);
|
||||
_httpContext.RenewUmbracoAuthTicket();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -229,7 +229,7 @@ namespace Umbraco.Web.Security
|
||||
|
||||
internal void UpdateLogin()
|
||||
{
|
||||
_httpContext.RenewUmbracoAuthTicket(UmbracoTimeOutInMinutes);
|
||||
_httpContext.RenewUmbracoAuthTicket();
|
||||
}
|
||||
|
||||
internal long GetTimeout()
|
||||
|
||||
@@ -839,6 +839,7 @@
|
||||
<Compile Include="WebApi\UmbracoAuthorizeAttribute.cs" />
|
||||
<Compile Include="WebApi\UmbracoAuthorizedApiController.cs" />
|
||||
<Compile Include="WebApi\Filters\ValidationFilterAttribute.cs" />
|
||||
<Compile Include="WebApi\Filters\UmbracoUserTimeoutFilterAttribute.cs" />
|
||||
<Compile Include="WebApi\WebApiHelper.cs" />
|
||||
<Compile Include="WebBootManager.cs" />
|
||||
<Compile Include="Routing\LegacyRequestInitializer.cs" />
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Linq;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Routing;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core;
|
||||
@@ -13,6 +14,7 @@ using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Editors;
|
||||
using Umbraco.Web.Routing;
|
||||
using Umbraco.Web.Security;
|
||||
using umbraco;
|
||||
@@ -173,7 +175,9 @@ namespace Umbraco.Web
|
||||
if (ShouldAuthenticateRequest(req, UmbracoContext.Current.OriginalRequestUrl))
|
||||
{
|
||||
var ticket = http.GetUmbracoAuthTicket();
|
||||
if (ticket != null && !ticket.Expired && http.RenewUmbracoAuthTicket())
|
||||
//if there was a ticket, it's not expired, its renewable - or it should not be renewed
|
||||
if (ticket != null && ticket.Expired == false
|
||||
&& (http.RenewUmbracoAuthTicket() || ShouldIgnoreTicketRenew(UmbracoContext.Current.OriginalRequestUrl, http)))
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -249,6 +253,35 @@ namespace Umbraco.Web
|
||||
return false;
|
||||
}
|
||||
|
||||
private static readonly ConcurrentHashSet<string> IgnoreTicketRenewUrls = new ConcurrentHashSet<string>();
|
||||
/// <summary>
|
||||
/// Determines if the authentication ticket should be renewed with a new timeout
|
||||
/// </summary>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <returns></returns>
|
||||
/// <remarks>
|
||||
/// We do not want to renew the ticket when we are checking for the user's remaining timeout.
|
||||
/// </remarks>
|
||||
internal static bool ShouldIgnoreTicketRenew(Uri url, HttpContextBase httpContext)
|
||||
{
|
||||
//initialize the ignore ticket urls - we don't need to lock this, it's concurrent and a hashset
|
||||
// we don't want to have to gen the url each request so this will speed things up a teeny bit.
|
||||
if (IgnoreTicketRenewUrls.Any() == false)
|
||||
{
|
||||
var urlHelper = new UrlHelper(new RequestContext(httpContext, new RouteData()));
|
||||
var checkSessionUrl = urlHelper.GetUmbracoApiServiceBaseUrl<AuthenticationController>(controller => controller.GetRemainingTimeoutSeconds());
|
||||
IgnoreTicketRenewUrls.Add(checkSessionUrl);
|
||||
}
|
||||
|
||||
if (IgnoreTicketRenewUrls.Any(x => url.AbsolutePath.StartsWith(x)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks the current request and ensures that it is routable based on the structure of the request and URI
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Globalization;
|
||||
using System.Web.Http.Filters;
|
||||
using Umbraco.Core.Security;
|
||||
|
||||
namespace Umbraco.Web.WebApi.Filters
|
||||
{
|
||||
/// <summary>
|
||||
/// This will check if the request is authenticated and if there's an auth ticket present we will
|
||||
/// add a custom header to the response indicating how many seconds are remaining for the current
|
||||
/// user's session. This allows us to keep track of a user's session effectively in the back office.
|
||||
/// </summary>
|
||||
public sealed class UmbracoUserTimeoutFilterAttribute : ActionFilterAttribute
|
||||
{
|
||||
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
|
||||
{
|
||||
base.OnActionExecuted(actionExecutedContext);
|
||||
|
||||
var httpContextAttempt = actionExecutedContext.Request.TryGetHttpContext();
|
||||
if (httpContextAttempt.Success)
|
||||
{
|
||||
var ticket = httpContextAttempt.Result.GetUmbracoAuthTicket();
|
||||
if (ticket != null && ticket.Expired == false)
|
||||
{
|
||||
var remainingSeconds = httpContextAttempt.Result.GetRemainingAuthSeconds();
|
||||
actionExecutedContext.Response.Headers.Add("X-Umb-User-Seconds", remainingSeconds.ToString(CultureInfo.InvariantCulture));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,39 @@ using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using System.Web.Http;
|
||||
using System.Web.Http.ModelBinding;
|
||||
using Umbraco.Core;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
|
||||
public static class HttpRequestMessageExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Tries to retreive the current HttpContext if one exists.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Attempt<HttpContextBase> TryGetHttpContext(this HttpRequestMessage request)
|
||||
{
|
||||
object context;
|
||||
if (request.Properties.TryGetValue("MS_HttpContext", out context))
|
||||
{
|
||||
var httpContext = context as HttpContextBase;
|
||||
if (httpContext != null)
|
||||
{
|
||||
return Attempt.Succeed(httpContext);
|
||||
}
|
||||
}
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
return Attempt<HttpContextBase>.Succeed(new HttpContextWrapper(HttpContext.Current));
|
||||
}
|
||||
|
||||
return Attempt<HttpContextBase>.Fail();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a 403 (Forbidden) response indicating that hte current user doesn't have access to the resource
|
||||
/// requested or the action it needs to take.
|
||||
|
||||
@@ -35,21 +35,7 @@ namespace Umbraco.Web.WebApi
|
||||
/// <returns></returns>
|
||||
protected Attempt<HttpContextBase> TryGetHttpContext()
|
||||
{
|
||||
object context;
|
||||
if (Request.Properties.TryGetValue("MS_HttpContext", out context))
|
||||
{
|
||||
var httpContext = context as HttpContextBase;
|
||||
if (httpContext != null)
|
||||
{
|
||||
return Attempt.Succeed(httpContext);
|
||||
}
|
||||
}
|
||||
if (HttpContext.Current != null)
|
||||
{
|
||||
return Attempt<HttpContextBase>.Succeed(new HttpContextWrapper(HttpContext.Current));
|
||||
}
|
||||
|
||||
return Attempt<HttpContextBase>.Fail();
|
||||
return Request.TryGetHttpContext();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Web.WebApi
|
||||
{
|
||||
/// <summary>
|
||||
/// Ensures authorization is successful for a back office user
|
||||
/// </summary>
|
||||
/// </summary>
|
||||
public sealed class UmbracoAuthorizeAttribute : AuthorizeAttribute
|
||||
{
|
||||
/// <summary>
|
||||
|
||||
@@ -8,6 +8,14 @@ using umbraco.BusinessLogic;
|
||||
|
||||
namespace Umbraco.Web.WebApi
|
||||
{
|
||||
/// <summary>
|
||||
/// A base controller that ensures all requests are authorized - the user is logged in.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This controller will also append a custom header to the response if the user is logged in using forms authentication
|
||||
/// which indicates the seconds remaining before their timeout expires.
|
||||
/// </remarks>
|
||||
[UmbracoUserTimeoutFilter]
|
||||
[UmbracoAuthorize]
|
||||
public abstract class UmbracoAuthorizedApiController : UmbracoApiController
|
||||
{
|
||||
|
||||
@@ -252,12 +252,12 @@ namespace umbraco.BasePages
|
||||
|
||||
private void UpdateLogin()
|
||||
{
|
||||
Context.RenewUmbracoAuthTicket(UmbracoTimeOutInMinutes);
|
||||
Context.RenewUmbracoAuthTicket();
|
||||
}
|
||||
|
||||
public static void RenewLoginTimeout()
|
||||
{
|
||||
HttpContext.Current.RenewUmbracoAuthTicket(UmbracoTimeOutInMinutes);
|
||||
HttpContext.Current.RenewUmbracoAuthTicket();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user