public override string[] GetUsersInRole(string roleName) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); try { TableServiceContext svc = CreateDataServiceContext(); DataServiceQuery <RoleRow> queryObj = svc.CreateQuery <RoleRow>(_tableName); var query = (from user in queryObj where user.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && user.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && user.RowKey == SecUtility.Escape(roleName) select user).AsTableServiceQuery(); IEnumerable <RoleRow> userRows = query.Execute(); if (userRows == null) { // role does not exist; we are supposed to throw an exception here throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", roleName)); } List <RoleRow> l = new List <RoleRow>(userRows); if (l.Count == 0 || IsStaleRole(l, roleName)) { throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "The role {0} does not exist!", roleName)); } List <string> ret = new List <string>(); foreach (RoleRow user in l) { if (!string.IsNullOrEmpty(user.UserName)) { ret.Add(user.UserName); } } return(ret.ToArray()); } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
public override bool RoleExists(string roleName) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); try { TableStorageDataServiceContext svc = CreateDataServiceContext(); DataServiceQuery <RoleRow> queryObj = svc.CreateQuery <RoleRow>(_tableName); IEnumerable <RoleRow> query = from role in queryObj where role.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty) && role.RowKey == SecUtility.Escape(roleName) select role; TableStorageDataServiceQuery <RoleRow> q = new TableStorageDataServiceQuery <RoleRow>(query as DataServiceQuery <RoleRow>, _tableRetry); try { // this query addresses exactly one result // we thus should get an exception if there is no element q.ExecuteWithRetries(); return(true); } catch (DataServiceQueryException e) { HttpStatusCode s; if (TableStorageHelpers.EvaluateException(e, out s) && s == HttpStatusCode.NotFound) { return(false); } else { throw; } } } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
public override void CreateRole(string roleName) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); try { TableServiceContext svc = CreateDataServiceContext(); RoleRow newRole = new RoleRow(_applicationName, roleName, string.Empty); svc.AddObject(_tableName, newRole); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { // when retry policies are used we cannot distinguish between a conflict and success // so, in the case of a conflict, we just retrun success here if (e.InnerException is DataServiceClientException && (e.InnerException as DataServiceClientException).StatusCode == (int)HttpStatusCode.Conflict) { return; // the role already exists } throw new ProviderException("Error accessing role table.", e); } }
private RoleRow GetUserInRole(DataServiceContext svc, string rolename, string username) { SecUtility.CheckParameter(ref username, true, true, true, Constants.MaxTableUsernameLength, "username"); SecUtility.CheckParameter(ref rolename, true, true, true, MaxTableRoleNameLength, "rolename"); try { DataServiceQuery <RoleRow> queryObj = svc.CreateQuery <RoleRow>(_tableName); IEnumerable <RoleRow> query = from user in queryObj where user.PartitionKey == SecUtility.CombineToKey(_applicationName, username) && user.RowKey == SecUtility.Escape(rolename) select user; TableStorageDataServiceQuery <RoleRow> q = new TableStorageDataServiceQuery <RoleRow>(query as DataServiceQuery <RoleRow>, _tableRetry); try { IEnumerable <RoleRow> userRows = q.ExecuteAllWithRetries(); return(userRows.First()); } catch (DataServiceQueryException e) { HttpStatusCode s; if (TableStorageHelpers.EvaluateException(e, out s) && s == HttpStatusCode.NotFound) { return(null); } else { throw; } } } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
public override bool RoleExists(string roleName) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); try { TableServiceContext svc = CreateDataServiceContext(); DataServiceQuery <RoleRow> queryObj = svc.CreateQuery <RoleRow>(_tableName); var query = (from role in queryObj where role.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty) && role.RowKey == SecUtility.Escape(roleName) select role).AsTableServiceQuery(); try { // this query addresses exactly one result // we thus should get an exception if there is no element return(query.Execute().Any()); } catch (InvalidOperationException e) { if ((e.InnerException is DataServiceClientException && (e.InnerException as DataServiceClientException).StatusCode == (int)HttpStatusCode.NotFound) || (e.InnerException.InnerException is DataServiceClientException && (e.InnerException.InnerException as DataServiceClientException).StatusCode == (int)HttpStatusCode.NotFound)) { return(false); } else { throw; } } } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
// we don't use the retry policy itself in this function because out parameters are not well handled by // retry policies private SessionStateStoreData GetSession(HttpContext context, string id, out bool locked, out TimeSpan lockAge, out object lockId, out SessionStateActions actions, bool exclusive) { Debug.Assert(context != null); SecUtility.CheckParameter(ref id, true, true, false, Configuration.MaxStringPropertySizeInChars, "id"); SessionRow session = null; int curRetry = 0; bool retry = false; // Assign default values to out parameters locked = false; lockId = null; lockAge = TimeSpan.Zero; actions = SessionStateActions.None; do { retry = false; try { TableServiceContext svc = CreateDataServiceContext(); session = GetSession(id, svc); // Assign default values to out parameters locked = false; lockId = null; lockAge = TimeSpan.Zero; actions = SessionStateActions.None; // if the blob does not exist, we return null // ASP.NET will call the corresponding method for creating the session if (session == null) { return(null); } if (session.Initialized == false) { Debug.Assert(session.Locked == false); actions = SessionStateActions.InitializeItem; session.Initialized = true; } session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); if (exclusive) { if (!session.Locked) { if (session.Lock == Int32.MaxValue) { session.Lock = 0; } else { session.Lock++; } session.LockDateUtc = DateTime.UtcNow; } lockId = session.Lock; locked = session.Locked; session.Locked = true; } lockAge = DateTime.UtcNow.Subtract(session.LockDateUtc); lockId = session.Lock; if (locked == true) { return(null); } // let's try to write this back to the data store // in between, someone else could have written something to the store for the same session // we retry a number of times; if all fails, we throw an exception svc.UpdateObject(session); svc.SaveChangesWithRetries(); } catch (InvalidOperationException e) { // precondition fails indicates problems with the status code if (e.InnerException is DataServiceClientException && (e.InnerException as DataServiceClientException).StatusCode == (int)HttpStatusCode.PreconditionFailed) { retry = true; } else { throw new ProviderException("Error accessing the data store.", e); } } } while (retry && curRetry++ < NumRetries); // ok, now we have successfully written back our state // we can now read the blob // note that we do not need to care about read/write locking when accessing the // blob because each time we write a new session we create a new blob with a different name SessionStateStoreData result = null; MemoryStream stream = null; StreamReader reader = null; BlobProperties properties; try { try { stream = _blobProvider.GetBlobContent(session.BlobName, out properties); } catch (Exception e) { throw new ProviderException("Couldn't read session blob!", e); } reader = new StreamReader(stream); if (actions == SessionStateActions.InitializeItem) { // Return an empty SessionStateStoreData result = new SessionStateStoreData(new SessionStateItemCollection(), SessionStateUtility.GetSessionStaticObjects(context), session.Timeout); } else { // Read Items, StaticObjects, and Timeout from the file byte[] items = Convert.FromBase64String(reader.ReadLine()); byte[] statics = Convert.FromBase64String(reader.ReadLine()); int timeout = session.Timeout; // Deserialize the session result = DeserializeSession(items, statics, timeout); } } finally { if (stream != null) { stream.Close(); } if (reader != null) { reader.Close(); } } return(result); }
public override void SetAndReleaseItemExclusive(HttpContext context, string id, SessionStateStoreData item, object lockId, bool newItem) { Debug.Assert(context != null); SecUtility.CheckParameter(ref id, true, true, false, Configuration.MaxStringPropertySizeInChars, "id"); _providerRetry(() => { TableServiceContext svc = CreateDataServiceContext(); SessionRow session; if (!newItem) { session = GetSession(id, svc); if (session == null || session.Lock != (int)lockId) { Debug.Assert(false); return; } } else { session = new SessionRow(id, _applicationName); session.Lock = 1; session.LockDateUtc = DateTime.UtcNow; } session.Initialized = true; Debug.Assert(session.Timeout >= 0); session.Timeout = item.Timeout; session.ExpiresUtc = DateTime.UtcNow.AddMinutes(session.Timeout); session.Locked = false; // yes, we always create a new blob here session.BlobName = GetBlobNamePrefix(id) + Guid.NewGuid().ToString("N"); // Serialize the session and write the blob byte[] items, statics; SerializeSession(item, out items, out statics); string serializedItems = Convert.ToBase64String(items); string serializedStatics = Convert.ToBase64String(statics); MemoryStream output = new MemoryStream(); StreamWriter writer = new StreamWriter(output); try { writer.WriteLine(serializedItems); writer.WriteLine(serializedStatics); writer.Flush(); // for us, it shouldn't matter whether newItem is set to true or false // because we always create the entire blob and cannot append to an // existing one _blobProvider.UploadStream(session.BlobName, output); writer.Close(); output.Close(); } catch (Exception e) { if (!newItem) { ReleaseItemExclusive(svc, session, lockId); } throw new ProviderException("Error accessing the data store.", e); } finally { if (writer != null) { writer.Close(); } if (output != null) { output.Close(); } } if (newItem) { svc.AddObject(_tableName, session); svc.SaveChangesWithRetries(); } else { // Unlock the session and save changes ReleaseItemExclusive(svc, session, lockId); } }); }
// the username to match can be in a format that varies between providers // for this implementation, a syntax similar to the one used in the SQL provider is applied // "user%" will return all users in a role that start with the string "user" // the % sign can only appear at the end of the usernameToMatch parameter // because the current version of the table storage service does not support StartsWith in LINQ queries, // calling this function can cause significant network trafic when '%' is used in the usernameToMach // parameter public override string[] FindUsersInRole(string roleName, string usernameToMatch) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); SecUtility.CheckParameter(ref usernameToMatch, true, true, false, Constants.MaxTableUsernameLength, "usernameToMatch"); bool startswith = false; if (usernameToMatch.Contains('%')) { if (usernameToMatch.IndexOf('%') != usernameToMatch.Length - 1) { throw new ArgumentException("The TableStorageRoleProvider only supports search strings that contain '%' as the last character!"); } usernameToMatch = usernameToMatch.Substring(0, usernameToMatch.Length - 1); startswith = true; } try { TableStorageDataServiceContext svc = CreateDataServiceContext(); DataServiceQuery <RoleRow> queryObj = svc.CreateQuery <RoleRow>(_tableName); IEnumerable <RoleRow> query; if (startswith && string.IsNullOrEmpty(usernameToMatch)) { // get all users in the role query = from userRole in queryObj where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && userRole.RowKey == SecUtility.Escape(roleName) select userRole; } else if (startswith) { // get all users in the role that start with the specified string (we cannot restrict the query more because StartsWith is not supported) // we cannot include the username to search for in the key, because the key might e escaped query = from userRole in queryObj where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && userRole.RowKey == SecUtility.Escape(roleName) && (userRole.UserName.CompareTo(usernameToMatch) >= 0 || userRole.UserName == string.Empty) select userRole; } else { // get a specific user query = from userRole in queryObj where (userRole.PartitionKey == SecUtility.CombineToKey(_applicationName, usernameToMatch) || userRole.PartitionKey == SecUtility.CombineToKey(_applicationName, string.Empty)) && userRole.RowKey == SecUtility.Escape(roleName) select userRole; } TableStorageDataServiceQuery <RoleRow> q = new TableStorageDataServiceQuery <RoleRow>(query as DataServiceQuery <RoleRow>, _tableRetry); IEnumerable <RoleRow> userRows = q.ExecuteAllWithRetries(); if (userRows == null) { throw new ProviderException("The role does not exist!"); } List <RoleRow> l = new List <RoleRow>(userRows); if (l.Count == 0) { // the role does not exist throw new ProviderException("The role does not exist!"); } RoleRow role; if (IsStaleRole(l, out role)) { throw new ProviderException("The role does not exist!"); } List <string> ret = new List <string>(); foreach (RoleRow row in l) { if (row != role) { if (startswith && !string.IsNullOrEmpty(usernameToMatch) && !row.UserName.StartsWith(usernameToMatch, StringComparison.Ordinal)) { continue; } ret.Add(row.UserName); } } return(ret.ToArray()); } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }
public override bool DeleteRole(string roleName, bool throwOnPopulatedRole) { SecUtility.CheckParameter(ref roleName, true, true, true, MaxTableRoleNameLength, "rolename"); try { TableStorageDataServiceContext svc = CreateDataServiceContext(); DataServiceQuery <RoleRow> queryObj = svc.CreateQuery <RoleRow>(_tableName); IEnumerable <RoleRow> query = from userRole in queryObj where userRole.PartitionKey.CompareTo(SecUtility.EscapedFirst(_applicationName)) >= 0 && userRole.PartitionKey.CompareTo(SecUtility.NextComparisonString(SecUtility.EscapedFirst(_applicationName))) < 0 && userRole.RowKey == SecUtility.Escape(roleName) select userRole; TableStorageDataServiceQuery <RoleRow> q = new TableStorageDataServiceQuery <RoleRow>(query as DataServiceQuery <RoleRow>, _tableRetry); IEnumerable <RoleRow> userRows = q.ExecuteAllWithRetries(); if (userRows == null) { return(false); } List <RoleRow> l = new List <RoleRow>(userRows); if (l.Count == 0) { // the role does not exist return(false); } RoleRow role; if (IsStaleRole(l, out role)) { return(false); } if (l.Count > 1 && throwOnPopulatedRole) { throw new ProviderException("Cannot delete populated role."); } svc.DeleteObject(role); svc.SaveChangesWithRetries(); // lets try to remove all remaining elements in the role foreach (RoleRow row in l) { if (row != role) { try { svc.DeleteObject(row); svc.SaveChangesWithRetries(); } catch (InvalidOperationException ex) { HttpStatusCode status; if (TableStorageHelpers.EvaluateException(ex, out status) && (status == HttpStatusCode.NoContent || status == HttpStatusCode.NotFound)) { // this element already was already deleted by another process or during a failed retry // this is not a fatal error; continue deleting elements Log.Write(EventKind.Warning, string.Format(CultureInfo.InstalledUICulture, "The user {0} does not exist in the role {1}.", row.UserName, row.RoleName)); } else { throw new ProviderException(string.Format(CultureInfo.InstalledUICulture, "Error deleting user {0} from role {1}.", row.UserName, row.RoleName)); } } } } return(true); } catch (InvalidOperationException e) { throw new ProviderException("Error while accessing the data store.", e); } }