Fixed bug in SqlServerSettingsStorageProvider and AclManager: ACL entries were kept in memory, causing problems with web-farm deployments.

This commit is contained in:
Dario Solera 2010-01-02 10:56:08 +00:00
parent bdcb8179e8
commit 2d8ce95810
12 changed files with 726 additions and 261 deletions

View file

@ -18,7 +18,6 @@ namespace ScrewTurn.Wiki.Plugins.SqlCommon {
private const int MaxParametersInQuery = 50;
private IAclManager aclManager;
private AclStorerBase aclStorer;
/// <summary>
/// Initializes the Storage Provider.
@ -29,9 +28,7 @@ namespace ScrewTurn.Wiki.Plugins.SqlCommon {
public new void Init(IHostV30 host, string config) {
base.Init(host, config);
aclManager = new StandardAclManager();
aclStorer = new SqlAclStorer(aclManager, LoadDataInternal, DeleteEntries, StoreEntries);
aclStorer.LoadData();
aclManager = new SqlAclManager(StoreEntry, DeleteEntries, RenameAclResource, RetrieveAllAclEntries, RetrieveAclEntriesForResource, RetrieveAclEntriesForSubject);
}
/// <summary>
@ -1070,131 +1067,6 @@ namespace ScrewTurn.Wiki.Plugins.SqlCommon {
get { return aclManager; }
}
/// <summary>
/// Converts a <see cref="T:Value" /> to its corresponding character representation.
/// </summary>
/// <param name="value">The <see cref="T:Value" />.</param>
/// <returns>The character representation.</returns>
private static char AclEntryValueToChar(Value value) {
switch(value) {
case Value.Grant:
return 'G';
case Value.Deny:
return 'D';
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Converts a character representation of a <see cref="T:Value" /> back to the enum value.
/// </summary>
/// <param name="c">The character representation.</param>
/// <returns>The <see cref="T:Value" />.</returns>
private static Value AclEntryValueFromChar(char c) {
switch(char.ToUpperInvariant(c)) {
case 'G':
return Value.Grant;
case 'D':
return Value.Deny;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Loads data from storage.
/// </summary>
/// <returns>The loaded ACL entries.</returns>
private AclEntry[] LoadDataInternal() {
ICommandBuilder builder = GetCommandBuilder();
// Sort order is not relevant
string query = QueryBuilder.NewQuery(builder).SelectFrom("AclEntry");
DbCommand command = builder.GetCommand(connString, query, new List<Parameter>());
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List<AclEntry> result = new List<AclEntry>(50);
while(reader.Read()) {
result.Add(new AclEntry(reader["Resource"] as string, reader["Action"] as string, reader["Subject"] as string,
AclEntryValueFromChar(((string)reader["Value"])[0])));
}
CloseReader(command, reader);
return result.ToArray();
}
else return null;
}
/// <summary>
/// Deletes some entries.
/// </summary>
/// <param name="entries">The entries to delete.</param>
private void DeleteEntries(AclEntry[] entries) {
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
QueryBuilder queryBuilder = new QueryBuilder(builder);
foreach(AclEntry entry in entries) {
string query = queryBuilder.DeleteFrom("AclEntry");
query = queryBuilder.Where(query, "Resource", WhereOperator.Equals, "Resource");
query = queryBuilder.AndWhere(query, "Action", WhereOperator.Equals, "Action");
query = queryBuilder.AndWhere(query, "Subject", WhereOperator.Equals, "Subject");
List<Parameter> parameters = new List<Parameter>(3);
parameters.Add(new Parameter(ParameterType.String, "Resource", entry.Resource));
parameters.Add(new Parameter(ParameterType.String, "Action", entry.Action));
parameters.Add(new Parameter(ParameterType.String, "Subject", entry.Subject));
DbCommand command = builder.GetCommand(transaction, query, parameters);
if(ExecuteNonQuery(command, false) != 1) {
RollbackTransaction(transaction);
return;
}
}
CommitTransaction(transaction);
}
/// <summary>
/// Stores some entries.
/// </summary>
/// <param name="entries">The entries to store.</param>
private void StoreEntries(AclEntry[] entries) {
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
QueryBuilder queryBuilder = new QueryBuilder(builder);
foreach(AclEntry entry in entries) {
string query = queryBuilder.InsertInto("AclEntry", new string[] { "Resource", "Action", "Subject", "Value" }, new string[] { "Resource", "Action", "Subject", "Value" });
List<Parameter> parameters = new List<Parameter>(3);
parameters.Add(new Parameter(ParameterType.String, "Resource", entry.Resource));
parameters.Add(new Parameter(ParameterType.String, "Action", entry.Action));
parameters.Add(new Parameter(ParameterType.String, "Subject", entry.Subject));
parameters.Add(new Parameter(ParameterType.Char, "Value", AclEntryValueToChar(entry.Value)));
DbCommand command = builder.GetCommand(transaction, query, parameters);
if(ExecuteNonQuery(command, false) != 1) {
RollbackTransaction(transaction);
return;
}
}
CommitTransaction(transaction);
}
/// <summary>
/// Stores the outgoing links of a page, overwriting existing data.
/// </summary>
@ -1430,6 +1302,243 @@ namespace ScrewTurn.Wiki.Plugins.SqlCommon {
#endregion
#region AclManager backend methods
/// <summary>
/// Converts a <see cref="T:Value" /> to its corresponding character representation.
/// </summary>
/// <param name="value">The <see cref="T:Value" />.</param>
/// <returns>The character representation.</returns>
private static char AclEntryValueToChar(Value value) {
switch(value) {
case Value.Grant:
return 'G';
case Value.Deny:
return 'D';
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Converts a character representation of a <see cref="T:Value" /> back to the enum value.
/// </summary>
/// <param name="c">The character representation.</param>
/// <returns>The <see cref="T:Value" />.</returns>
private static Value AclEntryValueFromChar(char c) {
switch(char.ToUpperInvariant(c)) {
case 'G':
return Value.Grant;
case 'D':
return Value.Deny;
default:
throw new NotSupportedException();
}
}
/// <summary>
/// Retrieves all ACL entries.
/// </summary>
/// <returns>The ACL entries.</returns>
private AclEntry[] RetrieveAllAclEntries() {
ICommandBuilder builder = GetCommandBuilder();
// Sort order is not relevant
string query = QueryBuilder.NewQuery(builder).SelectFrom("AclEntry");
DbCommand command = builder.GetCommand(connString, query, new List<Parameter>());
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List<AclEntry> result = new List<AclEntry>(50);
while(reader.Read()) {
result.Add(new AclEntry(reader["Resource"] as string, reader["Action"] as string, reader["Subject"] as string,
AclEntryValueFromChar(((string)reader["Value"])[0])));
}
CloseReader(command, reader);
return result.ToArray();
}
else return null;
}
/// <summary>
/// Retrieves all ACL entries for a resource.
/// </summary>
/// <param name="resource">The resource.</param>
/// <returns>The ACL entries for the resource.</returns>
private AclEntry[] RetrieveAclEntriesForResource(string resource) {
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
// Sort order is not relevant
string query = queryBuilder.SelectFrom("AclEntry");
query = queryBuilder.Where(query, "Resource", WhereOperator.Equals, "Resource");
List<Parameter> parameters = new List<Parameter>(1);
parameters.Add(new Parameter(ParameterType.String, "Resource", resource));
DbCommand command = builder.GetCommand(connString, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List<AclEntry> result = new List<AclEntry>(50);
while(reader.Read()) {
result.Add(new AclEntry(reader["Resource"] as string, reader["Action"] as string, reader["Subject"] as string,
AclEntryValueFromChar(((string)reader["Value"])[0])));
}
CloseReader(command, reader);
return result.ToArray();
}
else return null;
}
/// <summary>
/// Retrieves all ACL entries for a subject.
/// </summary>
/// <param name="subject">The subject.</param>
/// <returns>The ACL entries for the subject.</returns>
private AclEntry[] RetrieveAclEntriesForSubject(string subject) {
ICommandBuilder builder = GetCommandBuilder();
QueryBuilder queryBuilder = new QueryBuilder(builder);
// Sort order is not relevant
string query = queryBuilder.SelectFrom("AclEntry");
query = queryBuilder.Where(query, "Subject", WhereOperator.Equals, "Subject");
List<Parameter> parameters = new List<Parameter>(1);
parameters.Add(new Parameter(ParameterType.String, "Subject", subject));
DbCommand command = builder.GetCommand(connString, query, parameters);
DbDataReader reader = ExecuteReader(command);
if(reader != null) {
List<AclEntry> result = new List<AclEntry>(50);
while(reader.Read()) {
result.Add(new AclEntry(reader["Resource"] as string, reader["Action"] as string, reader["Subject"] as string,
AclEntryValueFromChar(((string)reader["Value"])[0])));
}
CloseReader(command, reader);
return result.ToArray();
}
else return null;
}
/// <summary>
/// Deletes some ACL entries.
/// </summary>
/// <param name="entries">The entries to delete.</param>
/// <returns><c>true</c> if one or more entries were deleted, <c>false</c> otherwise.</returns>
private bool DeleteEntries(AclEntry[] entries) {
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
QueryBuilder queryBuilder = new QueryBuilder(builder);
foreach(AclEntry entry in entries) {
string query = queryBuilder.DeleteFrom("AclEntry");
query = queryBuilder.Where(query, "Resource", WhereOperator.Equals, "Resource");
query = queryBuilder.AndWhere(query, "Action", WhereOperator.Equals, "Action");
query = queryBuilder.AndWhere(query, "Subject", WhereOperator.Equals, "Subject");
List<Parameter> parameters = new List<Parameter>(3);
parameters.Add(new Parameter(ParameterType.String, "Resource", entry.Resource));
parameters.Add(new Parameter(ParameterType.String, "Action", entry.Action));
parameters.Add(new Parameter(ParameterType.String, "Subject", entry.Subject));
DbCommand command = builder.GetCommand(transaction, query, parameters);
if(ExecuteNonQuery(command, false) <= 0) {
RollbackTransaction(transaction);
return false;
}
}
CommitTransaction(transaction);
return true;
}
/// <summary>
/// Stores a ACL entry.
/// </summary>
/// <param name="entry">The entry to store.</param>
/// <returns><c>true</c> if the entry was stored, <c>false</c> otherwise.</returns>
private bool StoreEntry(AclEntry entry) {
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.InsertInto("AclEntry", new string[] { "Resource", "Action", "Subject", "Value" }, new string[] { "Resource", "Action", "Subject", "Value" });
List<Parameter> parameters = new List<Parameter>(3);
parameters.Add(new Parameter(ParameterType.String, "Resource", entry.Resource));
parameters.Add(new Parameter(ParameterType.String, "Action", entry.Action));
parameters.Add(new Parameter(ParameterType.String, "Subject", entry.Subject));
parameters.Add(new Parameter(ParameterType.Char, "Value", AclEntryValueToChar(entry.Value)));
DbCommand command = builder.GetCommand(transaction, query, parameters);
if(ExecuteNonQuery(command, false) != 1) {
RollbackTransaction(transaction);
return false;
}
CommitTransaction(transaction);
return true;
}
/// <summary>
/// Renames a ACL resource.
/// </summary>
/// <param name="resource">The resource to rename.</param>
/// <param name="newName">The new name of the resource.</param>
/// <returns><c>true</c> if one or more entries weere updated, <c>false</c> otherwise.</returns>
private bool RenameAclResource(string resource, string newName) {
ICommandBuilder builder = GetCommandBuilder();
DbConnection connection = builder.GetConnection(connString);
DbTransaction transaction = BeginTransaction(connection);
QueryBuilder queryBuilder = new QueryBuilder(builder);
string query = queryBuilder.Update("AclEntry", new[] { "Resource" }, new[] { "ResourceNew" });
query = queryBuilder.Where(query, "Resource", WhereOperator.Equals, "ResourceOld");
List<Parameter> parameters = new List<Parameter>(2);
parameters.Add(new Parameter(ParameterType.String, "ResourceNew", newName));
parameters.Add(new Parameter(ParameterType.String, "ResourceOld", resource));
DbCommand command = builder.GetCommand(transaction, query, parameters);
if(ExecuteNonQuery(command, false) <= 0) {
RollbackTransaction(transaction);
return false;
}
CommitTransaction(transaction);
return true;
}
#endregion
}
}