Skip to content

Commit

Permalink
More Auth Refactoring.
Browse files Browse the repository at this point in the history
RequiredPermissions & RequiredRoles now fallback to UserAuthRepo on failed validation
Added RequestLogger
Added UserAuthId cookie to response
Added X-Method-Override feature
  • Loading branch information
mythz committed Jan 22, 2012
1 parent 78cffae commit 3711518
Show file tree
Hide file tree
Showing 27 changed files with 314 additions and 97 deletions.
12 changes: 12 additions & 0 deletions src/ServiceStack.Interfaces/ServiceHost/ICanResolve.cs
@@ -0,0 +1,12 @@
namespace ServiceStack.ServiceHost
{
public interface IResolver
{
/// <summary>
/// Resolve a dependency from the AppHost's IOC
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T TryResolve<T>();
}
}
9 changes: 1 addition & 8 deletions src/ServiceStack.Interfaces/ServiceHost/IHttpRequest.cs
Expand Up @@ -9,20 +9,13 @@ namespace ServiceStack.ServiceHost
/// <summary>
/// A thin wrapper around ASP.NET or HttpListener's HttpRequest
/// </summary>
public interface IHttpRequest
public interface IHttpRequest : IResolver
{
/// <summary>
/// The underlying ASP.NET or HttpListener HttpRequest
/// </summary>
object OriginalRequest { get; }

/// <summary>
/// Resolve a dependency from the AppHost's IOC
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T TryResolve<T>();

/// <summary>
/// The name of the service being called (e.g. Request DTO Name)
/// </summary>
Expand Down
15 changes: 15 additions & 0 deletions src/ServiceStack.Interfaces/ServiceHost/IRequestLogger.cs
@@ -0,0 +1,15 @@
namespace ServiceStack.ServiceHost
{
/// <summary>
/// Log every service request
/// </summary>
public interface IRequestLogger
{
/// <summary>
/// Log a request
/// </summary>
/// <param name="requestContext"></param>
/// <param name="requestDto"></param>
void Log(IRequestContext requestContext, object requestDto);
}
}
2 changes: 2 additions & 0 deletions src/ServiceStack.Interfaces/ServiceStack.Interfaces.csproj
Expand Up @@ -211,9 +211,11 @@
<Compile Include="SearchIndex\FullTextIndexAttribute.cs" />
<Compile Include="SearchIndex\FullTextIndexDocumentAttribute.cs" />
<Compile Include="SearchIndex\FullTextIndexFieldAttribute.cs" />
<Compile Include="ServiceHost\ICanResolve.cs" />
<Compile Include="ServiceHost\ICookies.cs" />
<Compile Include="ServiceHost\IHasRequestFilter.cs" />
<Compile Include="ServiceHost\IHasResponseFilter.cs" />
<Compile Include="ServiceHost\IRequestLogger.cs" />
<Compile Include="ServiceHost\IRestPatchService.cs" />
<Compile Include="ServiceHost\EndpointAttributes.cs" />
<Compile Include="ServiceHost\Feature.cs" />
Expand Down
7 changes: 7 additions & 0 deletions src/ServiceStack.ServiceInterface/Auth/AuthProvider.cs
Expand Up @@ -4,6 +4,7 @@
using ServiceStack.Common.Web;
using ServiceStack.Configuration;
using ServiceStack.Logging;
using ServiceStack.ServiceHost;
using ServiceStack.Text;

namespace ServiceStack.ServiceInterface.Auth
Expand Down Expand Up @@ -73,6 +74,12 @@ protected virtual void SaveUserAuth(IServiceBase authService, IAuthSession sessi
}

authRepo.SaveUserAuth(session);

var httpRes = authService.RequestContext.Get<IHttpResponse>();
if (httpRes != null)
{
httpRes.Cookies.AddPermanentCookie(HttpHeaders.XUserAuthId, session.UserAuthId);
}
}

public virtual void OnSaveUserAuth(IServiceBase authService, IAuthSession session) { }
Expand Down
1 change: 1 addition & 0 deletions src/ServiceStack.ServiceInterface/AuthFeature.cs
Expand Up @@ -10,6 +10,7 @@ namespace ServiceStack.ServiceInterface
public class AuthFeature
{
public const string AdminRole = "Admin";
public static bool AddUserIdHttpHeader = true;

public static void Init(IAppHost appHost, Func<IAuthSession> sessionFactory, params IAuthProvider[] authProviders)
{
Expand Down
29 changes: 17 additions & 12 deletions src/ServiceStack.ServiceInterface/AuthenticateAttribute.cs
Expand Up @@ -15,7 +15,7 @@ namespace ServiceStack.ServiceInterface
/// Indicates that the request dto, which is associated with this attribute,
/// requires authentication.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method /*MVC Actions*/, Inherited = false, AllowMultiple = false)]
public class AuthenticateAttribute : RequestFilterAttribute
{
public string Provider { get; set; }
Expand Down Expand Up @@ -59,17 +59,7 @@ public override void Execute(IHttpRequest req, IHttpResponse res, object request
return;
}

var userPass = req.GetBasicAuthUserAndPassword();
if (userPass != null)
{
var authService = req.TryResolve<AuthService>();
authService.RequestContext = new HttpRequestContext(req, res, requestDto);
var response = authService.Post(new Auth.Auth {
provider = BasicAuthProvider.Name,
UserName = userPass.Value.Key,
Password = userPass.Value.Value
});
}
AuthenticateIfBasicAuth(req, res);

using (var cache = req.GetCacheClient())
{
Expand All @@ -86,5 +76,20 @@ public override void Execute(IHttpRequest req, IHttpResponse res, object request
}
}
}

public static void AuthenticateIfBasicAuth(IHttpRequest req, IHttpResponse res)
{
var userPass = req.GetBasicAuthUserAndPassword();
if (userPass != null)
{
var authService = req.TryResolve<AuthService>();
authService.RequestContext = new HttpRequestContext(req, res, null);
var response = authService.Post(new Auth.Auth {
provider = BasicAuthProvider.Name,
UserName = userPass.Value.Key,
Password = userPass.Value.Value
});
}
}
}
}
4 changes: 1 addition & 3 deletions src/ServiceStack.ServiceInterface/IServiceBase.cs
Expand Up @@ -3,7 +3,7 @@

namespace ServiceStack.ServiceInterface
{
public interface IServiceBase
public interface IServiceBase : IResolver
{
IAppHost AppHost { get; set; }

Expand All @@ -14,8 +14,6 @@ public interface IServiceBase
/// <returns></returns>
T ResolveService<T>();

T TryResolve<T>();

IRequestContext RequestContext { get; }
}
}
40 changes: 26 additions & 14 deletions src/ServiceStack.ServiceInterface/RequiredPermissionAttribute.cs
Expand Up @@ -12,8 +12,8 @@ namespace ServiceStack.ServiceInterface
/// <summary>
/// Indicates that the request dto, which is associated with this attribute,
/// can only execute, if the user has specific permissions.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method /*MVC Actions*/, Inherited = false, AllowMultiple = true)]
public class RequiredPermissionAttribute : RequestFilterAttribute
{
public List<string> RequiredPermissions { get; set; }
Expand All @@ -32,25 +32,37 @@ public RequiredPermissionAttribute(ApplyTo applyTo, params string[] permissions)

public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
AuthenticateAttribute.AuthenticateIfBasicAuth(req, res);

var session = req.GetSession();
if (HasAllPermissions(session)) return;

var userAuthRepo = req.TryResolve<IUserAuthRepository>();
var userAuth = userAuthRepo.GetUserAuth(session, null);
session.UpdateSession(userAuth);

if (HasAllPermissions(session))
{
req.SaveSession(session);
return;
}
if (HasAllPermissions(req, session)) return;

res.StatusCode = (int)HttpStatusCode.Unauthorized;
res.StatusDescription = "Invalid Permissions";
res.Close();
}

private bool HasAllPermissions(IAuthSession session)
public bool HasAllPermissions(IHttpRequest req, IAuthSession session, IUserAuthRepository userAuthRepo=null)
{
if (HasAllPermissions(session)) return true;

if (userAuthRepo == null)
userAuthRepo = req.TryResolve<IUserAuthRepository>();

if (userAuthRepo == null) return false;

var userAuth = userAuthRepo.GetUserAuth(session, null);
session.UpdateSession(userAuth);

if (HasAllPermissions(session))
{
req.SaveSession(session);
return true;
}
return false;
}

public bool HasAllPermissions(IAuthSession session)
{
return this.RequiredPermissions
.All(requiredPermission => session != null
Expand Down
33 changes: 22 additions & 11 deletions src/ServiceStack.ServiceInterface/RequiredRoleAttribute.cs
Expand Up @@ -16,7 +16,7 @@ namespace ServiceStack.ServiceInterface
/// Indicates that the request dto, which is associated with this attribute,
/// can only execute, if the user has specific roles.
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = true)]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method /*MVC Actions*/, Inherited = false, AllowMultiple = true)]
public class RequiredRoleAttribute : RequestFilterAttribute
{
public List<string> RequiredRoles { get; set; }
Expand All @@ -32,29 +32,40 @@ public RequiredRoleAttribute(ApplyTo applyTo, params string[] roles)
this.RequiredRoles = roles.ToList();
this.ApplyTo = applyTo;
}



public override void Execute(IHttpRequest req, IHttpResponse res, object requestDto)
{
AuthenticateAttribute.AuthenticateIfBasicAuth(req, res);

var session = req.GetSession();
if (HasAllRoles(session)) return;
if (HasAllRoles(req, session)) return;

res.StatusCode = (int)HttpStatusCode.Unauthorized;
res.StatusDescription = "Invalid Role";
res.Close();
}

public bool HasAllRoles(IHttpRequest req, IAuthSession session, IUserAuthRepository userAuthRepo=null)
{
if (HasAllRoles(session)) return true;

if (userAuthRepo == null)
userAuthRepo = req.TryResolve<IUserAuthRepository>();

if (userAuthRepo == null) return false;

var userAuthRepo = req.TryResolve<IUserAuthRepository>();
var userAuth = userAuthRepo.GetUserAuth(session, null);
session.UpdateSession(userAuth);

if (HasAllRoles(session))
{
req.SaveSession(session);
return;
return true;
}

res.StatusCode = (int)HttpStatusCode.Unauthorized;
res.StatusDescription = "Invalid Role";
res.Close();
return false;
}

private bool HasAllRoles(IAuthSession session)
public bool HasAllRoles(IAuthSession session)
{
return this.RequiredRoles
.All(requiredRole => session != null
Expand Down
5 changes: 5 additions & 0 deletions src/ServiceStack.ServiceInterface/RestServiceBase.cs
Expand Up @@ -50,6 +50,7 @@ public object Get(TRequest request)
{
try
{
OnEachRequest(request);
OnBeforeExecute(request);
return OnAfterExecute(OnGet(request));
}
Expand Down Expand Up @@ -86,6 +87,7 @@ public object Put(TRequest request)
{
try
{
OnEachRequest(request);
OnBeforeExecute(request);
return OnAfterExecute(OnPut(request));
}
Expand Down Expand Up @@ -122,6 +124,7 @@ public object Post(TRequest request)
{
try
{
OnEachRequest(request);
OnBeforeExecute(request);
return OnAfterExecute(OnPost(request));
}
Expand Down Expand Up @@ -158,6 +161,7 @@ public object Delete(TRequest request)
{
try
{
OnEachRequest(request);
OnBeforeExecute(request);
return OnAfterExecute(OnDelete(request));
}
Expand Down Expand Up @@ -194,6 +198,7 @@ public object Patch(TRequest request)
{
try
{
OnEachRequest(request);
OnBeforeExecute(request);
return OnAfterExecute(OnPatch(request));
}
Expand Down
27 changes: 24 additions & 3 deletions src/ServiceStack.ServiceInterface/ServiceBase.cs
Expand Up @@ -37,8 +37,7 @@ public abstract class ServiceBase<TRequest>
/// Combined service error logs are maintained in 'urn:ServiceErrors:All'
/// </summary>
public const string CombinedServiceLogId = "All";



/// <summary>
/// Can be overriden to supply Custom 'ServiceName' error logs
/// </summary>
Expand All @@ -65,7 +64,26 @@ public virtual IAppHost AppHost

public IRequestContext RequestContext { get; set; }

public ISessionFactory SessionFactory { get; set; }
public ISessionFactory SessionFactory { get; set; }

public IRequestLogger RequestLogger { get; set; }

/// <summary>
/// Easy way to log all requests
/// </summary>
/// <param name="requestDto"></param>
protected void OnEachRequest(object requestDto)
{
if (this.RequestLogger == null) return;
try
{
RequestLogger.Log(this.RequestContext, requestDto);
}
catch (Exception ex)
{
Log.Error("Error while logging request: " + requestDto.Dump(), ex);
}
}

private ISession session;
public ISession Session
Expand Down Expand Up @@ -164,6 +182,7 @@ public object Execute(TRequest request)
{
try
{
OnEachRequest(request);
OnBeforeExecute(request);
return OnAfterExecute(Run(request));
}
Expand Down Expand Up @@ -263,6 +282,8 @@ public virtual object ExecuteAsync(TRequest request)
return Execute(request);
}

OnEachRequest(request);

//Capture and persist this async request on this Services 'In Queue'
//for execution after this request has been completed
using (var producer = MessageFactory.CreateMessageProducer())
Expand Down

0 comments on commit 3711518

Please sign in to comment.