Skip to content

Commit 35c721e

Browse files
committed
Add support for CredentialsAuthValidator.SkipPasswordVerificationForPrivateRequests to allow private requests to auth without a password
1 parent a79ad5d commit 35c721e

6 files changed

Lines changed: 107 additions & 31 deletions

File tree

src/ServiceStack/Auth/CredentialsAuthProvider.cs

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,19 @@ public CredentialsAuthValidator()
1818
}
1919
}
2020

21+
private class PrivateAuthValidator : AbstractValidator<Authenticate>
22+
{
23+
public PrivateAuthValidator()
24+
{
25+
RuleFor(x => x.UserName).NotEmpty();
26+
}
27+
}
28+
2129
public static string Name = AuthenticateService.CredentialsProvider;
2230
public static string Realm = "/auth/" + AuthenticateService.CredentialsProvider;
2331

32+
public bool SkipPasswordVerificationForPrivateRequests { get; set; }
33+
2434
public CredentialsAuthProvider()
2535
{
2636
Provider = Name;
@@ -44,20 +54,25 @@ public virtual bool TryAuthenticate(IServiceBase authService, string userName, s
4454
if (IsAccountLocked(authRepo, userAuth))
4555
throw new AuthenticationException("This account has been locked");
4656

47-
var holdSessionId = session.Id;
48-
session.PopulateWith(userAuth); //overwrites session.Id
49-
session.Id = holdSessionId;
50-
session.IsAuthenticated = true;
51-
session.UserAuthId = userAuth.Id.ToString(CultureInfo.InvariantCulture);
52-
session.ProviderOAuthAccess = authRepo.GetUserAuthDetails(session.UserAuthId)
53-
.ConvertAll(x => (IAuthTokens)x);
57+
PopulateSession(authRepo, userAuth, session);
5458

5559
return true;
5660
}
5761

5862
return false;
5963
}
6064

65+
private static void PopulateSession(IUserAuthRepository authRepo, IUserAuth userAuth, IAuthSession session)
66+
{
67+
var holdSessionId = session.Id;
68+
session.PopulateWith(userAuth); //overwrites session.Id
69+
session.Id = holdSessionId;
70+
session.IsAuthenticated = true;
71+
session.UserAuthId = userAuth.Id.ToString(CultureInfo.InvariantCulture);
72+
session.ProviderOAuthAccess = authRepo.GetUserAuthDetails(session.UserAuthId)
73+
.ConvertAll(x => (IAuthTokens) x);
74+
}
75+
6176
public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
6277
{
6378
if (request != null)
@@ -73,6 +88,12 @@ public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Auth
7388

7489
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
7590
{
91+
if (SkipPasswordVerificationForPrivateRequests && authService.Request.IsPrivateRequest())
92+
{
93+
new PrivateAuthValidator().ValidateAndThrow(request);
94+
return AuthenticatePrivateRequest(authService, session, request.UserName, request.Password, request.Continue);
95+
}
96+
7697
new CredentialsAuthValidator().ValidateAndThrow(request);
7798
return Authenticate(authService, session, request.UserName, request.Password, request.Continue);
7899
}
@@ -115,6 +136,38 @@ protected object Authenticate(IServiceBase authService, IAuthSession session, st
115136
throw HttpError.Unauthorized(ErrorMessages.InvalidUsernameOrPassword);
116137
}
117138

139+
protected object AuthenticatePrivateRequest(
140+
IServiceBase authService, IAuthSession session, string userName, string password, string referrerUrl)
141+
{
142+
var authRepo = authService.TryResolve<IAuthRepository>().AsUserAuthRepository(authService.GetResolver());
143+
144+
var userAuth = authRepo.GetUserAuthByUserName(userName);
145+
if (userAuth == null)
146+
throw HttpError.Unauthorized(ErrorMessages.InvalidUsernameOrPassword);
147+
148+
if (IsAccountLocked(authRepo, userAuth))
149+
throw new AuthenticationException("This account has been locked");
150+
151+
PopulateSession(authRepo, userAuth, session);
152+
153+
session.IsAuthenticated = true;
154+
155+
if (session.UserAuthName == null)
156+
session.UserAuthName = userName;
157+
158+
var response = OnAuthenticated(authService, session, null, null);
159+
if (response != null)
160+
return response;
161+
162+
return new AuthenticateResponse
163+
{
164+
UserId = session.UserAuthId,
165+
UserName = userName,
166+
SessionId = session.Id,
167+
ReferrerUrl = referrerUrl
168+
};
169+
}
170+
118171
public override IHttpResult OnAuthenticated(IServiceBase authService, IAuthSession session, IAuthTokens tokens, Dictionary<string, string> authInfo)
119172
{
120173
var userSession = session as AuthUserSession;

src/ServiceStack/HostContext.cs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -335,31 +335,34 @@ public static void RaiseUncaughtException(IRequest httpReq, IResponse httpRes, s
335335
/// </summary>
336336
public static T ResolveService<T>(HttpContextBase httpCtx=null) where T : class, IRequiresRequest
337337
{
338-
var service = AssertAppHost().Container.Resolve<T>();
339-
if (service == null) return null;
340-
service.Request = httpCtx != null ? httpCtx.ToRequest() : GetCurrentRequest();
341-
return service;
338+
var httpReq = httpCtx != null ? httpCtx.ToRequest() : GetCurrentRequest();
339+
return ResolveService(httpReq, AssertAppHost().Container.Resolve<T>());
342340
}
343341

344342
/// <summary>
345343
/// Resolves and auto-wires a ServiceStack Service from a HttpListenerContext.
346344
/// </summary>
347345
public static T ResolveService<T>(HttpListenerContext httpCtx) where T : class, IRequiresRequest
348346
{
349-
var service = AssertAppHost().Container.Resolve<T>();
350-
if (service == null) return null;
351-
service.Request = httpCtx.ToRequest();
352-
return service;
347+
return ResolveService(httpCtx.ToRequest(), AssertAppHost().Container.Resolve<T>());
353348
}
354349

355350
/// <summary>
356351
/// Resolves and auto-wires a ServiceStack Service.
357352
/// </summary>
358353
public static T ResolveService<T>(IHttpRequest httpReq) where T : class, IRequiresRequest
359354
{
360-
var service = AssertAppHost().Container.Resolve<T>();
361-
if (service == null) return null;
362-
service.Request = httpReq;
355+
return ResolveService(httpReq, AssertAppHost().Container.Resolve<T>());
356+
}
357+
358+
public static T ResolveService<T>(IRequest httpReq, T service)
359+
{
360+
var hasRequest = service as IRequiresRequest;
361+
if (hasRequest != null)
362+
{
363+
httpReq.SetPrivateRequest();
364+
hasRequest.Request = httpReq;
365+
}
363366
return service;
364367
}
365368

src/ServiceStack/Service.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,7 @@ public virtual T TryResolve<T>()
3939
public virtual T ResolveService<T>()
4040
{
4141
var service = TryResolve<T>();
42-
var requiresContext = service as IRequiresRequest;
43-
if (requiresContext != null)
44-
{
45-
requiresContext.Request = this.Request;
46-
}
47-
return service;
42+
return HostContext.ResolveService(this.Request, service);
4843
}
4944

5045
public object ExecuteRequest(object requestDto)
@@ -166,6 +161,8 @@ public virtual void Dispose()
166161
messageProducer.Dispose();
167162

168163
RequestContext.Instance.ReleaseDisposables();
164+
165+
Request.ReleaseIfPrivateRequest();
169166
}
170167
}
171168

src/ServiceStack/ServiceStackProvider.cs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -137,12 +137,7 @@ public virtual T TryResolve<T>()
137137
public virtual T ResolveService<T>()
138138
{
139139
var service = TryResolve<T>();
140-
var requiresContext = service as IRequiresRequest;
141-
if (requiresContext != null)
142-
{
143-
requiresContext.Request = Request;
144-
}
145-
return service;
140+
return HostContext.ResolveService(Request, service);
146141
}
147142

148143
public object Execute(object requestDto)

tests/ServiceStack.AuthWeb.Tests/AppHost.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ private void ConfigureAuth(Container container)
108108
// LoadUserAuthFilter = LoadUserAuthInfo,
109109
// AllowAllWindowsAuthUsers = true
110110
//},
111-
new CredentialsAuthProvider(), //HTML Form post of UserName/Password credentials
111+
new CredentialsAuthProvider { //HTML Form post of UserName/Password credentials
112+
SkipPasswordVerificationForPrivateRequests = true,
113+
},
112114
new TwitterAuthProvider(appSettings), //Sign-in with Twitter
113115
new FacebookAuthProvider(appSettings), //Sign-in with Facebook
114116
new DigestAuthProvider(appSettings), //Sign-in with Digest Auth
@@ -287,6 +289,26 @@ public override object Authenticate(IServiceBase authService, IAuthSession sessi
287289
}
288290
}
289291

292+
[Route("/privateauth")]
293+
public class PrivateAuth
294+
{
295+
public string UserName { get; set; }
296+
}
297+
298+
public class PrivateAuthService : Service
299+
{
300+
public object Any(PrivateAuth request)
301+
{
302+
using (var service = base.ResolveService<AuthenticateService>())
303+
{
304+
return service.Post(new Authenticate {
305+
provider = AuthenticateService.CredentialsProvider,
306+
UserName = request.UserName,
307+
});
308+
}
309+
}
310+
}
311+
290312
//Provide extra validation for the registration process
291313
public class CustomRegisterPlugin : IPlugin
292314
{

tests/ServiceStack.AuthWeb.Tests/default.cshtml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@
117117
<input type="submit"/>
118118
</form>
119119

120+
<h3>Private Auth</h3>
121+
<form action="/privateauth">
122+
<input type="text" name="Username" value="demis.bellot@gmail.com"/>
123+
<input type="submit"/>
124+
</form>
125+
120126
<h3>Register New User</h3>
121127
<form action="/register" method="POST">
122128
<input type="text" name="Username" placeholder="UserName" value="NewUser"/>

0 commit comments

Comments
 (0)