Skip to content

Commit 91e9537

Browse files
committed
Merge pull request #18 from sgraphics/master
Added RavenUserAuthRepository for RavenDB authentication support
2 parents 1307cd2 + ef80727 commit 91e9537

19 files changed

Lines changed: 51879 additions & 118 deletions
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System.Reflection;
2+
using System.Runtime.CompilerServices;
3+
using System.Runtime.InteropServices;
4+
5+
// General Information about an assembly is controlled through the following
6+
// set of attributes. Change these attribute values to modify the information
7+
// associated with an assembly.
8+
[assembly: AssemblyTitle("ServiceStack.Authentication.Raven")]
9+
[assembly: AssemblyDescription("")]
10+
[assembly: AssemblyConfiguration("")]
11+
[assembly: AssemblyCompany("")]
12+
[assembly: AssemblyProduct("ServiceStack.Authentication.Raven")]
13+
[assembly: AssemblyCopyright("Copyright © 2013")]
14+
[assembly: AssemblyTrademark("")]
15+
[assembly: AssemblyCulture("")]
16+
17+
// Setting ComVisible to false makes the types in this assembly not visible
18+
// to COM components. If you need to access a type in this assembly from
19+
// COM, set the ComVisible attribute to true on that type.
20+
[assembly: ComVisible(false)]
21+
22+
// The following GUID is for the ID of the typelib if this project is exposed to COM
23+
[assembly: Guid("b4fb0d80-a627-4495-ac7e-6bba3f3a482a")]
24+
25+
// Version information for an assembly consists of the following four values:
26+
//
27+
// Major Version
28+
// Minor Version
29+
// Build Number
30+
// Revision
31+
//
32+
// You can specify all the values or you can default the Build and Revision Numbers
33+
// by using the '*' as shown below:
34+
// [assembly: AssemblyVersion("1.0.*")]
35+
[assembly: AssemblyVersion("1.0.0.0")]
36+
[assembly: AssemblyFileVersion("1.0.0.0")]
Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.Linq;
5+
using System.Text.RegularExpressions;
6+
using Raven.Client;
7+
using ServiceStack.Common;
8+
using ServiceStack.ServiceInterface.Auth;
9+
using ServiceStack.Text;
10+
11+
namespace ServiceStack.Authentication.Raven
12+
{
13+
public class RavenUserAuthRepository : IUserAuthRepository
14+
{
15+
//http://stackoverflow.com/questions/3588623/c-sharp-regex-for-a-username-with-a-few-restrictions
16+
public Regex ValidUserNameRegEx = new Regex(@"^(?=.{3,15}$)([A-Za-z0-9][._-]?)*$", RegexOptions.Compiled);
17+
18+
private IDocumentStore _documentStore;
19+
20+
public RavenUserAuthRepository(IDocumentStore documentStore)
21+
{
22+
_documentStore = documentStore;
23+
}
24+
25+
private void ValidateNewUser(UserAuth newUser, string password)
26+
{
27+
newUser.ThrowIfNull("newUser");
28+
password.ThrowIfNullOrEmpty("password");
29+
30+
if (newUser.UserName.IsNullOrEmpty() && newUser.Email.IsNullOrEmpty())
31+
throw new ArgumentNullException("UserName or Email is required");
32+
33+
if (!newUser.UserName.IsNullOrEmpty())
34+
{
35+
if (!ValidUserNameRegEx.IsMatch(newUser.UserName))
36+
throw new ArgumentException("UserName contains invalid characters", "UserName");
37+
}
38+
}
39+
40+
public UserAuth CreateUserAuth(UserAuth newUser, string password)
41+
{
42+
ValidateNewUser(newUser, password);
43+
44+
AssertNoExistingUser(newUser);
45+
46+
var saltedHash = new SaltedHash();
47+
string salt;
48+
string hash;
49+
saltedHash.GetHashAndSaltString(password, out hash, out salt);
50+
var digestHelper = new DigestAuthFunctions();
51+
newUser.DigestHA1Hash = digestHelper.CreateHa1(newUser.UserName, DigestAuthProvider.Realm, password);
52+
newUser.PasswordHash = hash;
53+
newUser.Salt = salt;
54+
newUser.CreatedDate = DateTime.UtcNow;
55+
newUser.ModifiedDate = newUser.CreatedDate;
56+
57+
using (var session = _documentStore.OpenSession())
58+
{
59+
session.Store(newUser);
60+
session.SaveChanges();
61+
}
62+
63+
return newUser;
64+
}
65+
66+
private void AssertNoExistingUser(UserAuth newUser, UserAuth exceptForExistingUser = null)
67+
{
68+
if (newUser.UserName != null)
69+
{
70+
var existingUser = GetUserAuthByUserName(newUser.UserName);
71+
if (existingUser != null
72+
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
73+
throw new ArgumentException("User {0} already exists".Fmt(newUser.UserName));
74+
}
75+
if (newUser.Email != null)
76+
{
77+
var existingUser = GetUserAuthByUserName(newUser.Email);
78+
if (existingUser != null
79+
&& (exceptForExistingUser == null || existingUser.Id != exceptForExistingUser.Id))
80+
throw new ArgumentException("Email {0} already exists".Fmt(newUser.Email));
81+
}
82+
}
83+
84+
public UserAuth UpdateUserAuth(UserAuth existingUser, UserAuth newUser, string password)
85+
{
86+
ValidateNewUser(newUser, password);
87+
88+
AssertNoExistingUser(newUser, existingUser);
89+
90+
var hash = existingUser.PasswordHash;
91+
var salt = existingUser.Salt;
92+
if (password != null)
93+
{
94+
var saltedHash = new SaltedHash();
95+
saltedHash.GetHashAndSaltString(password, out hash, out salt);
96+
}
97+
// If either one changes the digest hash has to be recalculated
98+
var digestHash = existingUser.DigestHA1Hash;
99+
if (password != null || existingUser.UserName != newUser.UserName)
100+
{
101+
var digestHelper = new DigestAuthFunctions();
102+
digestHash = digestHelper.CreateHa1(newUser.UserName, DigestAuthProvider.Realm, password);
103+
}
104+
newUser.Id = existingUser.Id;
105+
newUser.PasswordHash = hash;
106+
newUser.Salt = salt;
107+
newUser.DigestHA1Hash = digestHash;
108+
newUser.CreatedDate = existingUser.CreatedDate;
109+
newUser.ModifiedDate = DateTime.UtcNow;
110+
111+
using (var session = _documentStore.OpenSession())
112+
{
113+
session.Store(newUser);
114+
session.SaveChanges();
115+
}
116+
117+
return newUser;
118+
}
119+
120+
public UserAuth GetUserAuthByUserName(string userNameOrEmail)
121+
{
122+
using (var session = _documentStore.OpenSession())
123+
{
124+
var isEmail = userNameOrEmail.Contains("@");
125+
var userAuth = isEmail
126+
? session.Query<UserAuth>().FirstOrDefault(q => q.Email == userNameOrEmail)
127+
: session.Query<UserAuth>().FirstOrDefault(q => q.UserName == userNameOrEmail);
128+
129+
return userAuth;
130+
}
131+
}
132+
133+
public bool TryAuthenticate(string userName, string password, out UserAuth userAuth)
134+
{
135+
//userId = null;
136+
userAuth = GetUserAuthByUserName(userName);
137+
if (userAuth == null) return false;
138+
139+
var saltedHash = new SaltedHash();
140+
if (saltedHash.VerifyHashString(password, userAuth.PasswordHash, userAuth.Salt))
141+
{
142+
//userId = userAuth.Id.ToString(CultureInfo.InvariantCulture);
143+
return true;
144+
}
145+
146+
userAuth = null;
147+
return false;
148+
}
149+
150+
public bool TryAuthenticate(Dictionary<string, string> digestHeaders, string PrivateKey, int NonceTimeOut, string sequence, out UserAuth userAuth)
151+
{
152+
userAuth = GetUserAuthByUserName(digestHeaders["username"]);
153+
if (userAuth == null) return false;
154+
155+
var digestHelper = new DigestAuthFunctions();
156+
if (digestHelper.ValidateResponse(digestHeaders, PrivateKey, NonceTimeOut, userAuth.DigestHA1Hash, sequence))
157+
{
158+
return true;
159+
}
160+
userAuth = null;
161+
return false;
162+
}
163+
164+
public void LoadUserAuth(IAuthSession session, IOAuthTokens tokens)
165+
{
166+
session.ThrowIfNull("session");
167+
168+
var userAuth = GetUserAuth(session, tokens);
169+
LoadUserAuth(session, userAuth);
170+
}
171+
172+
private void LoadUserAuth(IAuthSession session, UserAuth userAuth)
173+
{
174+
if (userAuth == null) return;
175+
176+
var idSesije = session.Id; //first record session Id (original session Id)
177+
session.PopulateWith(userAuth); //here, original sessionId is overwritten with facebook user Id
178+
session.Id = idSesije; //we return Id of original session here
179+
180+
session.UserAuthId = userAuth.Id.ToString(CultureInfo.InvariantCulture);
181+
session.ProviderOAuthAccess = GetUserOAuthProviders(session.UserAuthId)
182+
.ConvertAll(x => (IOAuthTokens)x);
183+
184+
}
185+
186+
public UserAuth GetUserAuth(string userAuthId)
187+
{
188+
using (var session = _documentStore.OpenSession())
189+
{
190+
return session.Load<UserAuth>(userAuthId);
191+
}
192+
}
193+
194+
public void SaveUserAuth(IAuthSession authSession)
195+
{
196+
using (var session = _documentStore.OpenSession())
197+
{
198+
int idInt = int.Parse(authSession.UserAuthId);
199+
200+
var userAuth = !authSession.UserAuthId.IsNullOrEmpty()
201+
? session.Load<UserAuth>(idInt)
202+
: authSession.TranslateTo<UserAuth>();
203+
204+
if (userAuth.Id == default(int) && !authSession.UserAuthId.IsNullOrEmpty())
205+
userAuth.Id = idInt;
206+
207+
userAuth.ModifiedDate = DateTime.UtcNow;
208+
if (userAuth.CreatedDate == default(DateTime))
209+
userAuth.CreatedDate = userAuth.ModifiedDate;
210+
211+
session.Store(userAuth);
212+
session.SaveChanges();
213+
}
214+
}
215+
216+
public void SaveUserAuth(UserAuth userAuth)
217+
{
218+
using (var session = _documentStore.OpenSession())
219+
{
220+
userAuth.ModifiedDate = DateTime.UtcNow;
221+
if (userAuth.CreatedDate == default(DateTime))
222+
userAuth.CreatedDate = userAuth.ModifiedDate;
223+
224+
session.Store(userAuth);
225+
session.SaveChanges();
226+
}
227+
}
228+
229+
public List<UserOAuthProvider> GetUserOAuthProviders(string userAuthId)
230+
{
231+
using (var session = _documentStore.OpenSession())
232+
{
233+
var id = int.Parse(userAuthId);
234+
return session.Query<UserOAuthProvider>().Where(q => q.UserAuthId == id).OrderBy(x => x.ModifiedDate).ToList();
235+
}
236+
}
237+
238+
public UserAuth GetUserAuth(IAuthSession authSession, IOAuthTokens tokens)
239+
{
240+
if (!authSession.UserAuthId.IsNullOrEmpty())
241+
{
242+
var userAuth = GetUserAuth(authSession.UserAuthId);
243+
if (userAuth != null) return userAuth;
244+
}
245+
if (!authSession.UserAuthName.IsNullOrEmpty())
246+
{
247+
var userAuth = GetUserAuthByUserName(authSession.UserAuthName);
248+
if (userAuth != null) return userAuth;
249+
}
250+
251+
if (tokens == null || tokens.Provider.IsNullOrEmpty() || tokens.UserId.IsNullOrEmpty())
252+
return null;
253+
254+
using (var session = _documentStore.OpenSession())
255+
{
256+
var oAuthProvider = session
257+
.Query<UserOAuthProvider>()
258+
.FirstOrDefault<UserOAuthProvider>(q => q.Provider == tokens.Provider && q.UserId == tokens.UserId);
259+
260+
if (oAuthProvider != null)
261+
{
262+
var userAuth = session.Load<UserAuth>(oAuthProvider.UserAuthId);
263+
return userAuth;
264+
}
265+
return null;
266+
}
267+
}
268+
269+
public string CreateOrMergeAuthSession(IAuthSession authSession, IOAuthTokens tokens)
270+
{
271+
var userAuth = GetUserAuth(authSession, tokens) ?? new UserAuth();
272+
273+
using (var session = _documentStore.OpenSession())
274+
{
275+
var oAuthProvider = session.Query<UserOAuthProvider>().FirstOrDefault(q => q.Provider == tokens.Provider && q.UserId == tokens.UserId);
276+
277+
if (oAuthProvider == null)
278+
{
279+
oAuthProvider = new UserOAuthProvider
280+
{
281+
Provider = tokens.Provider,
282+
UserId = tokens.UserId,
283+
};
284+
}
285+
286+
oAuthProvider.PopulateMissing(tokens);
287+
userAuth.PopulateMissing(oAuthProvider);
288+
289+
userAuth.ModifiedDate = DateTime.UtcNow;
290+
if (userAuth.CreatedDate == default(DateTime))
291+
userAuth.CreatedDate = userAuth.ModifiedDate;
292+
293+
session.Store(userAuth);
294+
session.SaveChanges();
295+
296+
oAuthProvider.UserAuthId = userAuth.Id;
297+
298+
if (oAuthProvider.CreatedDate == default(DateTime))
299+
oAuthProvider.CreatedDate = userAuth.ModifiedDate;
300+
oAuthProvider.ModifiedDate = userAuth.ModifiedDate;
301+
302+
session.Store(oAuthProvider);
303+
session.SaveChanges();
304+
305+
return oAuthProvider.UserAuthId.ToString(CultureInfo.InvariantCulture);
306+
}
307+
}
308+
}
309+
}

0 commit comments

Comments
 (0)