public ILoginProcess GetActiveProcess(IEntitySession session, LoginProcessType processType, string token) { Util.Check(!string.IsNullOrWhiteSpace(token), "Process token may not be null"); var context = session.Context; //get process without expiration checking, we check it later var query = from p in session.EntitySet <ILoginProcess>() where p.ProcessType == processType && p.Token == token select p; var process = query.FirstOrDefault(); context.ThrowIfNull(process, ClientFaultCodes.ObjectNotFound, "Process", "Login process not found."); if (process.Status != LoginProcessStatus.Active) { return(null); } if (process.FailCount >= _settings.MaxFailCount) { process.Status = LoginProcessStatus.AbortedAsFraud; OnLoginEvent(context, LoginEventType.ProcessAborted, process.Login, "Aborted - too many failures."); session.SaveChanges(); return(null); } var userName = process.Login.UserName; if (process.ExpiresOn < App.TimeService.UtcNow) { process.Status = LoginProcessStatus.Expired; OnLoginEvent(context, LoginEventType.ProcessAborted, process.Login, "Expired."); session.SaveChanges(); return(null); } return(process); }
public void AddBookToOrder(IEntitySession session, Guid orderId, Guid bookId, int quantity) { var order = session.GetEntity<IBookOrder>(orderId, LockOptions.ForUpdate); var book = session.GetEntity<IBook>(bookId); var orderLine = session.NewEntity<IBookOrderLine>(); orderLine.Order = order; orderLine.Book = book; orderLine.Quantity = quantity; orderLine.Price = book.Price; order.Lines.Add(orderLine); order.Total = order.Lines.Sum(ol => ol.Price * ol.Quantity); session.SaveChanges(); }
public void AddBookToOrder(IEntitySession session, Guid orderId, Guid bookId, byte quantity) { var order = session.GetEntity <IBookOrder>(orderId, LockOptions.ForUpdate); var book = session.GetEntity <IBook>(bookId); var orderLine = session.NewEntity <IBookOrderLine>(); orderLine.Order = order; orderLine.Book = book; orderLine.Quantity = quantity; orderLine.Price = book.Price; order.Lines.Add(orderLine); order.Total = order.Lines.Sum(ol => ol.Price * ol.Quantity); session.SaveChanges(); }
}//method private void ImportBooksInCategory(BookCategory category, string keyword, int count) { var skip = 0; var currentCount = 0; while (currentCount < count) { var volumeSet = _client.GetVolumes(keyword, skip); skip += volumeSet.Items.Count; foreach (var volume in volumeSet.Items) { var vinfo = volume.VolumeInfo; if (string.IsNullOrWhiteSpace(vinfo.Publisher)) //some books don't have publisher, just skip these { continue; } var title = Trim(vinfo.Title, 120); if (_bookCache.ContainsKey(title)) { continue; } currentCount++; var ipub = GetCreatePublisher(vinfo.Publisher); var pubDate = ParsePublishedDate(vinfo.PublishedDate); var image = LoadImageFromUrl(vinfo.ImageLinks.Thumbnail); var price = GetPrice(volume.SaleInfo); var ibook = _session.NewBook(BookEdition.Paperback, category, title, vinfo.SubTitle, ipub, pubDate, price, coverImage: image); ibook.Abstract = vinfo.Description; _bookCache.Add(vinfo.Title, ibook); //parse authors if (vinfo.Authors != null) { foreach (var author in vinfo.Authors) { var iauth = GetCreateAuthor(author); if (iauth != null) { ibook.Authors.Add(iauth); } } } }//foreach volume } try { _session.SaveChanges(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine("Exception: " + ex.ToLogString()); throw; } }
// Can be called explicitly outside of migrations to unencrypt existing values public static void UnencryptFactorValues(IEntitySession session) { var loginConfig = session.Context.App.GetConfig <LoginModuleSettings>(); var errLog = session.Context.App.ErrorLog; int batchSize = 200; int skip = 0; try { var factors = session.EntitySet <ILoginExtraFactor>() .Where(f => f.FactorValue == null).OrderBy(f => f.CreatedOn) .Skip(skip).Take(batchSize).ToList(); skip += batchSize; if (factors.Count == 0) { return; } //preload all EncryptedValue records var encrIds = factors.Select(f => f.Info_Id).ToList(); var encrRecs = session.EntitySet <IEncryptedData>().Where(ed => encrIds.Contains(ed.Id)); foreach (var f in factors) { if (f.Info_Id == null || f.Info_Id.Value == Guid.Empty) { continue; } var ed = session.GetEntity <IEncryptedData>(f.Info_Id); //should be preloaded if (ed == null) { continue; } f.FactorValue = ed.DecryptString(loginConfig.EncryptionChannelName); f.Info_Id = null; } session.SaveChanges(); } catch (Exception ex) { if (errLog != null) { errLog.LogError(ex, session.Context); } if (!SuppressMigrationErrors) { throw; } } }
private void RunRandomReadWriteOp(Guid[] docIds, LockType readLock, LockType writeLock, int readWriteCount) { var rand = new Random(Thread.CurrentThread.ManagedThreadId); IEntitySession session = null; // Use context with its own buffered op log - all entries related to single load/update operation are buffered, // and then flushed together at the end of loop body; so they will appear together in the output file var ctx = new OperationContext(_app); ctx.Log = new BufferedLog(ctx.LogContext); for (int i = 0; i < readWriteCount; i++) { session = ctx.OpenSession(); var randomDocId = docIds[rand.Next(docIds.Length)]; IDoc iDoc; IDocDetail iDet; int randomOp = -1; try { Thread.Yield(); var randomValueName = "N" + rand.Next(5); randomOp = rand.Next(7); switch (randomOp) { case 0: case 1: //read and check total session.LogMessage("\r\n----------------- Load, check Doc total ---------------------"); iDoc = session.GetEntity <IDoc>(randomDocId, readLock); Thread.Yield(); //to let other thread mess it up var valuesSum = iDoc.Details.Sum(v => v.Value); if (valuesSum != iDoc.Total) { session.LogMessage("!!!! Sum error: Doc.Total: {0}, Sum(Value): {1}", iDoc.Total, valuesSum); Interlocked.Increment(ref _sumErrorCount); } Thread.Yield(); session.ReleaseLocks(); session.LogMessage("\r\n-------------- Completed Load/check total ---------------------"); break; case 2: case 3: case 4: //insert/update, we give it an edge over deletes, to have 2 upserts for 1 delete session.LogMessage("\r\n----------------- Update/insert DocDetail ---------------------"); iDoc = session.GetEntity <IDoc>(randomDocId, writeLock); Thread.Yield(); iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName); if (iDet == null) { iDet = session.NewEntity <IDocDetail>(); iDet.Doc = iDoc; iDet.Name = randomValueName; iDoc.Details.Add(iDet); //to correctly calculate total } iDet.Value = rand.Next(10); iDoc.Total = iDoc.Details.Sum(v => v.Value); Thread.Yield(); session.SaveChanges(); session.LogMessage("\r\n------Completed Update/insert doc detail ---------------------"); var entSession = (Vita.Entities.Runtime.EntitySession)session; if (entSession.CurrentConnection != null) { Debugger.Break(); //check that connection is closed and removed from session } break; case 6: //delete if exists session.LogMessage("\r\n----------------- Delete doc detail ---------------------"); //Note: deletes do not throw any errors - if record does not exist (had been just deleted), stored proc do not throw error iDoc = session.GetEntity <IDoc>(randomDocId, writeLock); Thread.Yield(); //allow others mess up iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName); if (iDet != null) { session.DeleteEntity(iDet); iDoc.Details.Remove(iDet); iDoc.Total = iDoc.Details.Sum(v => v.Value); session.SaveChanges(); // even if there's no changes, it will release lock } else { session.ReleaseLocks(); } session.LogMessage("\r\n----------------- Completed delete doc detail ---------------------"); break; }//switch } catch (Exception ex) { //most will be UniqueIndexViolation Debug.WriteLine(ex.ToLogString()); System.Threading.Interlocked.Increment(ref _updateErrorCount); session.Context.Log.AddEntry(new ErrorLogEntry(ctx.LogContext, ex)); var entSession = (Vita.Entities.Runtime.EntitySession)session; if (entSession.CurrentConnection != null) { entSession.CurrentConnection.Close(); } //session.Context.Log.Flush(); _app.Flush(); } finally { //_app.Flush(); } //session.Context.Log.Flush(); } //for i } //method
private void RunRandomReadWriteOp(Guid[] docIds, LockOptions readOptions, LockOptions writeOptions, int readWriteCount) { var rand = new Random(Thread.CurrentThread.ManagedThreadId); IEntitySession session = null; for (int i = 0; i < readWriteCount; i++) { var randomDocId = docIds[rand.Next(docIds.Length)]; var randomValueName = "N" + rand.Next(5); IDoc iDoc; IDocDetail iDet; int randomOp = -1; try { Thread.Yield(); session = _app.OpenSession(); randomOp = rand.Next(5); switch (randomOp) { case 0: case 1: //insert/update, we give it an edge over deletes, to have 2 upserts for 1 delete session.LogMessage("\r\n----------------- Update/insert value ---------------------"); iDoc = session.GetEntity <IDoc>(randomDocId, writeOptions); Thread.Yield(); iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName); if (iDet == null) { iDet = session.NewEntity <IDocDetail>(); iDet.Doc = iDoc; iDet.Name = randomValueName; iDoc.Details.Add(iDet); //to correctly calculate total } iDet.Value = rand.Next(10); iDoc.Total = iDoc.Details.Sum(v => v.Value); Thread.Yield(); session.SaveChanges(); var entSession = (Vita.Entities.Runtime.EntitySession)session; if (entSession.CurrentConnection != null) { Debugger.Break(); //check that connection is closed and removed from session } break; case 2: //delete if exists session.LogMessage("\r\n----------------- Delete value ---------------------"); //Note: deletes do not throw any errors - if record does not exist (had been just deleted), stored proc do not throw error iDoc = session.GetEntity <IDoc>(randomDocId, writeOptions); Thread.Yield(); //allow others mess up iDet = iDoc.Details.FirstOrDefault(iv => iv.Name == randomValueName); if (iDet != null) { session.DeleteEntity(iDet); iDoc.Details.Remove(iDet); iDoc.Total = iDoc.Details.Sum(v => v.Value); } Thread.Yield(); session.SaveChanges(); // even if there's no changes, it will release lock break; case 3: case 4: //read and check total session.LogMessage("\r\n----------------- Loading doc ---------------------"); iDoc = session.GetEntity <IDoc>(randomDocId, readOptions); Thread.Yield(); //to let other thread mess it up var valuesSum = iDoc.Details.Sum(v => v.Value); if (valuesSum != iDoc.Total) { session.LogMessage("!!!! Sum error: Doc.Total: {0}, Sum(Value): {1}", iDoc.Total, valuesSum); Interlocked.Increment(ref _sumErrorCount); } Thread.Yield(); session.ReleaseLocks(); break; }//switch WriteLog(session); } catch (Exception ex) { //most will be UniqueIndexViolation Debug.WriteLine(ex.ToLogString()); System.Threading.Interlocked.Increment(ref _updateErrorCount); if (session != null) { WriteLog(session); var log = session.Context.LocalLog.GetAllAsText(); Debug.WriteLine(log); var entSession = (Vita.Entities.Runtime.EntitySession)session; if (entSession.CurrentConnection != null) { entSession.CurrentConnection.Close(); } } } } //for i } //method
public static void CreateUpdatePopularServers(IEntitySession session) { // Windows Live CreateOrUpdateServer(session, "WindowsLive", OAuthServerOptions.TokenReplaceLocalIpWithLocalHost, "https://www.live.com/", "https://login.live.com/oauth20_authorize.srf", "https://login.live.com/oauth20_token.srf", "https://login.live.com/oauth20_token.srf", // refresh URL, same as access token URL null, //no revoke URL "wl.basic wl.emails wl.photos wl.offline_access wl.signin", "https://msdn.microsoft.com/en-us/library/hh243647.aspx", "https://apis.live.net/v5.0/me", "id"); // Google // Specifics: Refresh token is returned only in the first request for access token CreateOrUpdateServer(session, "Google", OAuthServerOptions.OpenIdConnect | OAuthServerOptions.RevokeUseGetNoClientInfo, "http://www.google.com", "https://accounts.google.com/o/oauth2/v2/auth", "https://www.googleapis.com/oauth2/v4/token", "https://www.googleapis.com/oauth2/v4/token", "https://accounts.google.com/o/oauth2/revoke", //?token={token}", //revoke "profile email", "https://developers.google.com/identity/protocols/OAuth2WebServer", "https://www.googleapis.com/plus/v1/people/me", "id"); // Facebook // TODO: Investigage; looks like FB supports id_token (like in OpenIdConnect), but requires some twists // investigate why currently does not return id_token CreateOrUpdateServer(session, "Facebook", OAuthServerOptions.TokenUseGet | OAuthServerOptions.OpenIdConnect | OAuthServerOptions.TokenReplaceLocalIpWithLocalHost, "http://www.facebook.com", "https://www.facebook.com/dialog/oauth", "https://graph.facebook.com/v2.3/oauth/access_token", null, null, // no revoke permissions; FB says you can use 'DELETE /{user-id}/permissions', but it's out of the line of oauth "public_profile email", "https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow", "https://graph.facebook.com/v2.5/me", //basic profile "id"); // LinkedIn. Specifics: // 1. LinkedIn uses Get for access token endpoint (OAuth2 spec requires POST) CreateOrUpdateServer(session, "LinkedIn", OAuthServerOptions.TokenUseGet, "http://www.linkedin.com", "https://www.linkedin.com/oauth/v2/authorization", "https://www.linkedin.com/oauth/v2/accessToken", null, //refresh null, // revoke "r_basicprofile r_emailaddress rw_company_admin w_share", "https://developer.linkedin.com/docs/oauth2", "https://api.linkedin.com/v1/people/~?format=json", "id"); // Fitbit. // 1. Access token endpoint requries authorization header which is Base64 encoded 'clientid:clientsecret' CreateOrUpdateServer(session, "Fitbit", OAuthServerOptions.ClientInfoInAuthHeader, "https://www.fitbit.com", "https://www.fitbit.com/oauth2/authorize", "https://api.fitbit.com/oauth2/token", "https://api.fitbit.com/oauth2/token", // refresh token "https://api.fitbit.com/oauth2/revoke", // ?token={token}", //revoke, use POST "activity heartrate location nutrition profile settings sleep social weight", "https://dev.fitbit.com/docs/oauth2/", "https://api.fitbit.com/1/user/-/profile.json", "encodedId"); // Jawbone CreateOrUpdateServer(session, "Jawbone", OAuthServerOptions.TokenUseGet, "https://jawbone.com/", "https://jawbone.com/auth/oauth2/auth", "https://jawbone.com/auth/oauth2/token", "https://jawbone.com/auth/oauth2/token", null, //revoke "basic_read extended_read location_read friends_read mood_read mood_write move_read move_write " + "sleep_read sleep_write meal_read meal_write weight_read weight_write " + "generic_event_read generic_event_write heartrate_read", "https://jawbone.com/up/developer/authentication", "https://jawbone.com/nudge/api/v.1.1/users/@me", "user_xid"); // Yahoo. Specifics: it is impossible to test - it does not allow localhost as redirect URL. // There are fancy hacks/workarounds (creating localtest.me in hosts file) - but that's too much. // It is disabled here, but you can enable it if you need Yahoo /* * CreateOrUpdateServer(session, "Yahoo", * OAuthServerOptions.OpenIdConnect | OAuthServerOptions.RequestTokenClientInfoInAuthHeader | OAuthServerOptions.TokenReplaceLocalIpWithLocalHost, | "http://www.yahoo.com", | "https://api.login.yahoo.com/oauth2/request_auth", | "https://api.login.yahoo.com/oauth2/get_token", | "https://api.login.yahoo.com/oauth2/get_token", | "sdps-r mail-r", | "https://developer.yahoo.com/oauth2/guide/", | "https://social.yahooapis.com/v1/user/abcdef123/profile?format=json"); */ session.SaveChanges(); }
public static void CreateUpdatePopularServers(IEntitySession session) { // Windows Live CreateOrUpdateServer(session, "WindowsLive", OAuthServerOptions.TokenReplaceLocalIpWithLocalHost, "https://www.live.com/", "https://login.live.com/oauth20_authorize.srf", "https://login.live.com/oauth20_token.srf", "https://login.live.com/oauth20_token.srf", // refresh URL, same as access token URL "wl.basic wl.emails wl.photos wl.offline_access wl.signin", "https://msdn.microsoft.com/en-us/library/hh243647.aspx", "https://apis.live.net/v5.0/me", "id"); // Google // Specifics: Refresh token is returned only in the first request for access token CreateOrUpdateServer(session, "Google", OAuthServerOptions.OpenIdConnect, "http://www.google.com", "https://accounts.google.com/o/oauth2/v2/auth", "https://www.googleapis.com/oauth2/v4/token", "https://www.googleapis.com/oauth2/v4/token", "profile email", "https://developers.google.com/identity/protocols/OAuth2WebServer", "https://www.googleapis.com/plus/v1/people/me", "id"); // Facebook // TODO: Investigage; looks like FB supports id_token (like in OpenIdConnect), but requires some twists // investigate why currently does not return id_token CreateOrUpdateServer(session, "Facebook", OAuthServerOptions.TokenUseGet | OAuthServerOptions.OpenIdConnect | OAuthServerOptions.TokenReplaceLocalIpWithLocalHost, "http://www.facebook.com", "https://www.facebook.com/dialog/oauth", "https://graph.facebook.com/v2.3/oauth/access_token", null, "public_profile email", "https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow", "https://graph.facebook.com/v2.5/me", "id"); // LinkedIn. Specifics: // 1. LinkedIn uses Get for access token endpoint (OAuth2 spec requires POST) CreateOrUpdateServer(session, "LinkedIn", OAuthServerOptions.TokenUseGet, "http://www.linkedin.com", "https://www.linkedin.com/oauth/v2/authorization", "https://www.linkedin.com/oauth/v2/accessToken", null, "r_basicprofile r_emailaddress rw_company_admin w_share", "https://developer.linkedin.com/docs/oauth2", "https://api.linkedin.com/v1/people/~?format=json", "id"); // Fitbit. // 1. Access token endpoint requries authorization header which is Base64 encoded 'clientid:clientsecret' CreateOrUpdateServer(session, "Fitbit", OAuthServerOptions.RequestTokenClientInfoInAuthHeader, "https://www.fitbit.com", "https://www.fitbit.com/oauth2/authorize", "https://api.fitbit.com/oauth2/token", "https://api.fitbit.com/oauth2/token", // refresh token "activity heartrate location nutrition profile settings sleep social weight", "https://dev.fitbit.com/docs/oauth2/", "https://api.fitbit.com/1/user/-/profile.json", "encodedId"); // Jawbone CreateOrUpdateServer(session, "Jawbone", OAuthServerOptions.TokenUseGet, "https://jawbone.com/", "https://jawbone.com/auth/oauth2/auth", "https://jawbone.com/auth/oauth2/token", "https://jawbone.com/auth/oauth2/token", "basic_read extended_read location_read friends_read mood_read mood_write move_read move_write " + "sleep_read sleep_write meal_read meal_write weight_read weight_write " + "generic_event_read generic_event_write heartrate_read", "https://jawbone.com/up/developer/authentication", "https://jawbone.com/nudge/api/v.1.1/users/@me", "user_xid"); // Yahoo. Specifics: it is impossible to test - it does not allow localhost as redirect URL. // There are fancy hacks/workarounds (creating localtest.me in hosts file) - but that's too much. // It is disabled here, but you can enable it if you need Yahoo /* CreateOrUpdateServer(session, "Yahoo", OAuthServerOptions.OpenIdConnect | OAuthServerOptions.RequestTokenClientInfoInAuthHeader | OAuthServerOptions.TokenReplaceLocalIpWithLocalHost, "http://www.yahoo.com", "https://api.login.yahoo.com/oauth2/request_auth", "https://api.login.yahoo.com/oauth2/get_token", "https://api.login.yahoo.com/oauth2/get_token", "sdps-r mail-r", "https://developer.yahoo.com/oauth2/guide/", "https://social.yahooapis.com/v1/user/abcdef123/profile?format=json"); */ session.SaveChanges(); }
// If necessary, can be called explicitly outside of migrations to unencrypt existing values public static void UnencryptTokens(IEntitySession session) { var config = session.Context.App.GetConfig <OAuthClientSettings>(); var errLog = session.Context.App.ErrorLog; // Unencrypt client secret in remote server accounts - should be a smaill table var accts = session.EntitySet <IOAuthRemoteServerAccount>().Where(a => a.ClientSecret_Id != null).Take(100).ToList(); foreach (var acct in accts) { var ed = session.GetEntity <IEncryptedData>(acct.ClientSecret_Id); if (ed == null) { continue; } acct.ClientSecret = ed.DecryptString(config.EncryptionChannel); acct.ClientSecret_Id = null; } session.SaveChanges(); // Tokens - might be big table, process in batches int batchSize = 200; int skip = 0; try { var tokenRecs = session.EntitySet <IOAuthAccessToken>() .Where(tr => tr.AccessToken == null).OrderBy(tr => tr.RetrievedOn) .Skip(skip).Take(batchSize).ToList(); skip += batchSize; if (tokenRecs.Count == 0) { return; } //preload all EncryptedValue records var encrIds1 = tokenRecs.Where(tr => tr.AccessToken_Id != null).Select(tr => tr.AccessToken_Id.Value).ToList(); var encrIds2 = tokenRecs.Where(tr => tr.RefreshToken_Id != null).Select(tr => tr.RefreshToken_Id.Value).ToList(); encrIds1.AddRange(encrIds2); var encrRecs = session.EntitySet <IEncryptedData>().Where(ed => encrIds1.Contains(ed.Id)); foreach (var tr in tokenRecs) { //Access token var eId = tr.AccessToken_Id; if (eId != null && eId.Value != Guid.Empty) { var ed = session.GetEntity <IEncryptedData>(eId.Value); //should be preloaded if (ed != null) { tr.AccessToken = ed.DecryptString(config.EncryptionChannel); tr.AccessToken_Id = null; } } // Refresh token eId = tr.AccessToken_Id; if (eId != null && eId.Value != Guid.Empty) { var ed = session.GetEntity <IEncryptedData>(eId.Value); //should be preloaded if (ed != null) { tr.RefreshToken = ed.DecryptString(config.EncryptionChannel); tr.RefreshToken_Id = null; } } } //foreach tr session.SaveChanges(); } catch (Exception ex) { if (errLog != null) { errLog.LogError(ex, session.Context); } if (!SuppressMigrationErrors) { throw; } } } //method