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

@ -44,7 +44,7 @@
<HtmlHelpName>ScrewTurnWiki</HtmlHelpName>
<Language>en-US</Language>
<CopyrightHref>http://www.screwturn.eu</CopyrightHref>
<CopyrightText>Copyright (C)2006-2009 Dario Solera</CopyrightText>
<CopyrightText>Copyright (C)2006-2010 Dario Solera</CopyrightText>
<FeedbackEMailAddress />
<FeedbackEMailLinkText />
<HeaderText>ScrewTurn Wiki Developer's Documentation</HeaderText>

Binary file not shown.

View file

@ -0,0 +1,198 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Rhino.Mocks;
using ScrewTurn.Wiki.AclEngine;
namespace ScrewTurn.Wiki.Plugins.SqlCommon.Tests {
[TestFixture]
public class SqlAclManagerTests {
// Note: AclEntry overrides Equals
private MockRepository _mocks;
private StoreEntry _storeEntry;
private DeleteEntries _deleteEntries;
private RenameResource _renameResource;
private RetrieveAllEntries _retrieveAllEntries;
private RetrieveEntriesForResource _retrieveEntriesForResource;
private RetrieveEntriesForSubject _retrieveEntriesForSubject;
[SetUp]
public void SetUp() {
_mocks = new MockRepository();
_storeEntry = _mocks.StrictMock<StoreEntry>();
_deleteEntries = _mocks.StrictMock<DeleteEntries>();
_renameResource = _mocks.StrictMock<RenameResource>();
_retrieveAllEntries = _mocks.StrictMock<RetrieveAllEntries>();
_retrieveEntriesForResource = _mocks.StrictMock<RetrieveEntriesForResource>();
_retrieveEntriesForSubject = _mocks.StrictMock<RetrieveEntriesForSubject>();
}
[Test]
public void StoreEntry() {
_storeEntry.Stub(x => x(new AclEntry("res", "action", "subject", Value.Grant))).Return(true);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
Assert.IsTrue(manager.StoreEntry("res", "action", "subject", Value.Grant), "StoreEntry should return true");
_mocks.VerifyAll();
}
[Test]
public void DeleteEntry() {
_deleteEntries.Stub(x => x(new[] { new AclEntry("res", "action", "subject", Value.Deny) })).Return(true);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
Assert.IsTrue(manager.DeleteEntry("res", "action", "subject"), "DeleteEntry should return true");
_mocks.VerifyAll();
}
[Test]
public void DeleteEntriesForResource() {
AclEntry[] entries = new[] {
new AclEntry("res", "action1", "subject1", Value.Grant),
new AclEntry("res", "action1", "subject2", Value.Deny),
new AclEntry("res", "action2", "subject1", Value.Grant)
};
_retrieveEntriesForResource.Stub(x => x("res")).Return(entries);
_deleteEntries.Stub(x => x(entries)).Return(true);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
Assert.IsTrue(manager.DeleteEntriesForResource("res"), "DeleteEntriesForResource should return true");
_mocks.VerifyAll();
}
[Test]
public void DeleteEntriesForSubject() {
AclEntry[] entries = new[] {
new AclEntry("res1", "action1", "subject", Value.Grant),
new AclEntry("res1", "action1", "subject", Value.Deny),
new AclEntry("res2", "action2", "subject", Value.Grant)
};
_retrieveEntriesForSubject.Stub(x => x("subject")).Return(entries);
_deleteEntries.Stub(x => x(entries)).Return(true);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
Assert.IsTrue(manager.DeleteEntriesForSubject("subject"), "DeleteEntriesForSubject should return true");
_mocks.VerifyAll();
}
[Test]
public void RenameResource() {
_renameResource.Stub(x => x("res", "newName")).Return(true);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
Assert.IsTrue(manager.RenameResource("res", "newName"), "RenameResource should return true");
_mocks.VerifyAll();
}
[Test]
public void RetrieveAllEntries() {
AclEntry[] entries = new[] {
new AclEntry("res1", "action1", "subject", Value.Grant),
new AclEntry("res1", "action1", "subject", Value.Deny),
new AclEntry("res2", "action2", "subject", Value.Grant)
};
_retrieveAllEntries.Stub(x => { x(); }).Return(entries);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
// Returned array reference-equals entries
Assert.AreEqual(entries, manager.RetrieveAllEntries(), "Wrong array returned");
_mocks.VerifyAll();
}
[Test]
public void RetrieveEntriesForResource() {
AclEntry[] entries = new[] {
new AclEntry("res", "action1", "subject1", Value.Grant),
new AclEntry("res", "action1", "subject2", Value.Deny),
new AclEntry("res", "action2", "subject1", Value.Grant)
};
_retrieveEntriesForResource.Stub(x => x("res")).Return(entries);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
// Returned array reference-equals entries
Assert.AreEqual(entries, manager.RetrieveEntriesForResource("res"), "Wrong array returned");
_mocks.VerifyAll();
}
[Test]
public void RetrieveEntriesForSubject() {
AclEntry[] entries = new[] {
new AclEntry("res1", "action1", "subject", Value.Grant),
new AclEntry("res1", "action1", "subject", Value.Deny),
new AclEntry("res2", "action2", "subject", Value.Grant)
};
_retrieveEntriesForSubject.Stub(x => x("subject")).Return(entries);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
// Returned array reference-equals entries
Assert.AreEqual(entries, manager.RetrieveEntriesForSubject("subject"), "Wrong array returned");
_mocks.VerifyAll();
}
[Test]
public void TotalEntries() {
AclEntry[] entries = new[] {
new AclEntry("res1", "action1", "subject", Value.Grant),
new AclEntry("res1", "action1", "subject", Value.Deny),
new AclEntry("res2", "action2", "subject", Value.Grant)
};
_retrieveAllEntries.Stub(x => x()).Return(entries);
_mocks.ReplayAll();
SqlAclManager manager = new SqlAclManager(_storeEntry, _deleteEntries, _renameResource, _retrieveAllEntries, _retrieveEntriesForResource, _retrieveEntriesForSubject);
Assert.AreEqual(entries.Length, manager.TotalEntries, "Wrong entry count");
_mocks.VerifyAll();
}
}
}

View file

@ -1,43 +0,0 @@

using System;
using System.Collections.Generic;
using System.Text;
using ScrewTurn.Wiki.AclEngine;
using NUnit.Framework;
using Rhino.Mocks;
namespace ScrewTurn.Wiki.Plugins.SqlCommon.Tests {
[TestFixture]
public class SqlAclStorerTests {
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void Constructor_NullLoadData() {
MockRepository mocks = new MockRepository();
SqlAclStorer storer = new SqlAclStorer(mocks.StrictMock<IAclManager>(), null,
new DeleteEntries(e => { }), new StoreEntries(e => { }));
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void Constructor_NullDeleteEntries() {
MockRepository mocks = new MockRepository();
SqlAclStorer storer = new SqlAclStorer(mocks.StrictMock<IAclManager>(), new LoadData(() => { return null; }),
null, new StoreEntries(e => { }));
}
[Test]
[ExpectedException(typeof(ArgumentNullException))]
public void Constructor_NullStoreEntries() {
MockRepository mocks = new MockRepository();
SqlAclStorer storer = new SqlAclStorer(mocks.StrictMock<IAclManager>(), new LoadData(() => { return null; }),
new DeleteEntries(e => { }), null);
}
}
}

View file

@ -54,7 +54,7 @@
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="QueryBuilderTests.cs" />
<Compile Include="SqlAclStorerTests.cs" />
<Compile Include="SqlAclManagerTests.cs" />
<Compile Include="SqlIndexTests.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -0,0 +1,269 @@

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ScrewTurn.Wiki.AclEngine;
namespace ScrewTurn.Wiki.Plugins.SqlCommon {
/// <summary>
/// Implements a SQL ACL Manager.
/// </summary>
public class SqlAclManager : IAclManager {
// This class is similar to AclManagerBase in AclEngine
// but it does not work with in-memory data.
// All operations are actually handled by a backend database, via delegates
// The AclChanged event is never fired
private StoreEntry _storeEntry;
private DeleteEntries _deleteEntries;
private RenameResource _renameResource;
private RetrieveAllEntries _retrieveAllEntries;
private RetrieveEntriesForResource _retrieveEntriesForResource;
private RetrieveEntriesForSubject _retrieveEntriesForSubject;
/// <summary>
/// Initializes a new instance of the <see cref="T:SqlAclManager" /> class.
/// </summary>
/// <param name="storeEntry">The <see cref="StoreEntry"/> delegate.</param>
/// <param name="deleteEntries">The <see cref="DeleteEntries"/> delegate.</param>
/// <param name="renameResource">The <see cref="RenameResource"/> delegate.</param>
/// <param name="retrieveAllEntries">The <see cref="RetrieveAllEntries"/> delegate.</param>
/// <param name="retrieveEntriesForResource">The <see cref="RetrieveEntriesForResource"/> delegate.</param>
/// <param name="retrieveEntriesForSubject">The <see cref="RetrieveEntriesForSubject"/> delegate.</param>
public SqlAclManager(StoreEntry storeEntry, DeleteEntries deleteEntries, RenameResource renameResource,
RetrieveAllEntries retrieveAllEntries, RetrieveEntriesForResource retrieveEntriesForResource, RetrieveEntriesForSubject retrieveEntriesForSubject) {
if(storeEntry == null) throw new ArgumentNullException("storeEntry");
if(deleteEntries == null) throw new ArgumentNullException("deleteEntries");
if(renameResource == null) throw new ArgumentNullException("renameResource");
if(retrieveAllEntries == null) throw new ArgumentNullException("retrieveAllEntries");
if(retrieveEntriesForResource == null) throw new ArgumentNullException("retrieveEntriesForResource");
if(retrieveEntriesForSubject == null) throw new ArgumentNullException("retrieveEntriesForSubject");
_storeEntry = storeEntry;
_deleteEntries = deleteEntries;
_renameResource = renameResource;
_retrieveAllEntries = retrieveAllEntries;
_retrieveEntriesForResource = retrieveEntriesForResource;
_retrieveEntriesForSubject = retrieveEntriesForSubject;
}
/// <summary>
/// Handles the invokation of <see cref="IAclManager.AclChanged" /> event.
/// </summary>
/// <param name="entries">The changed entries.</param>
/// <param name="change">The change.</param>
[Obsolete]
private void OnAclChanged(AclEntry[] entries, Change change) {
if(AclChanged != null) {
AclChanged(this, new AclChangedEventArgs(entries, change));
}
}
/// <summary>
/// Stores a new ACL entry.
/// </summary>
/// <param name="resource">The controlled resource.</param>
/// <param name="action">The action on the controlled resource.</param>
/// <param name="subject">The subject whose access to the resource/action is controlled.</param>
/// <param name="value">The value of the entry.</param>
/// <returns><c>true</c> if the entry is stored, <c>false</c> otherwise.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are empty.</exception>
public bool StoreEntry(string resource, string action, string subject, Value value) {
if(resource == null) throw new ArgumentNullException("resource");
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
if(action == null) throw new ArgumentNullException("action");
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
if(subject == null) throw new ArgumentNullException("subject");
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
AclEntry entry = new AclEntry(resource, action, subject, value);
return _storeEntry(entry);
}
/// <summary>
/// Deletes an ACL entry.
/// </summary>
/// <param name="resource">The controlled resource.</param>
/// <param name="action">The action on the controlled resource.</param>
/// <param name="subject">The subject whose access to the resource/action is controlled.</param>
/// <returns><c>true</c> if the entry is deleted, <c>false</c> otherwise.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="resource"/>, <paramref name="action"/> or <paramref name="subject"/> are empty.</exception>
public bool DeleteEntry(string resource, string action, string subject) {
if(resource == null) throw new ArgumentNullException("resource");
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
if(action == null) throw new ArgumentNullException("action");
if(action.Length == 0) throw new ArgumentException("Action cannot be empty", "action");
if(subject == null) throw new ArgumentNullException("subject");
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
AclEntry entry = new AclEntry(resource, action, subject, Value.Deny);
return _deleteEntries(new[] { entry });
}
/// <summary>
/// Deletes all the ACL entries for a resource.
/// </summary>
/// <param name="resource">The controlled resource.</param>
/// <returns><c>true</c> if the entries are deleted, <c>false</c> otherwise.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="resource"/> is empty.</exception>
public bool DeleteEntriesForResource(string resource) {
if(resource == null) throw new ArgumentNullException("resource");
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
AclEntry[] entries = _retrieveEntriesForResource(resource);
return _deleteEntries(entries);
}
/// <summary>
/// Deletes all the ACL entries for a subject.
/// </summary>
/// <param name="subject">The subject.</param>
/// <returns><c>true</c> if the entries are deleted, <c>false</c> otherwise.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="subject"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="subject"/> is empty.</exception>
public bool DeleteEntriesForSubject(string subject) {
if(subject == null) throw new ArgumentNullException("subject");
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
AclEntry[] entries = _retrieveEntriesForSubject(subject);
return _deleteEntries(entries);
}
/// <summary>
/// Renames a resource.
/// </summary>
/// <param name="resource">The resource.</param>
/// <param name="newName">The new name of the resource.</param>
/// <returns><c>true</c> if the resource is renamed, <c>false</c> otherwise.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> or <paramref name="newName"/> are <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="resource"/> or <paramref name="newName"/> are empty.</exception>
public bool RenameResource(string resource, string newName) {
if(resource == null) throw new ArgumentNullException("resource");
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
if(newName == null) throw new ArgumentNullException("newName");
if(newName.Length == 0) throw new ArgumentException("New Name cannot be empty", "newName");
return _renameResource(resource, newName);
}
/// <summary>
/// Retrieves all the ACL entries for a resource.
/// </summary>
/// <returns>The entries.</returns>
public AclEntry[] RetrieveAllEntries() {
return _retrieveAllEntries();
}
/// <summary>
/// Retrieves all the ACL entries for a resource.
/// </summary>
/// <param name="resource">The resource.</param>
/// <returns>The entries.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="resource"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="resource"/> is empty.</exception>
public AclEntry[] RetrieveEntriesForResource(string resource) {
if(resource == null) throw new ArgumentNullException("resource");
if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource");
return _retrieveEntriesForResource(resource);
}
/// <summary>
/// Retrieves all the ACL entries for a subject.
/// </summary>
/// <param name="subject">The subject.</param>
/// <returns>The entries.</returns>
/// <exception cref="ArgumentNullException">If <paramref name="subject"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">If <paramref name="subject"/> is empty.</exception>
public AclEntry[] RetrieveEntriesForSubject(string subject) {
if(subject == null) throw new ArgumentNullException("subject");
if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject");
return _retrieveEntriesForSubject(subject);
}
/// <summary>
/// Initializes the manager data.
/// </summary>
/// <param name="entries">The ACL entries.</param>
/// <exception cref="ArgumentNullException">If <paramref name="entries"/> is <c>null</c>.</exception>
public void InitializeData(AclEntry[] entries) {
if(entries == null) throw new ArgumentNullException("entries");
}
/// <summary>
/// Gets the total number of ACL entries.
/// </summary>
public int TotalEntries {
get {
return RetrieveAllEntries().Length;
}
}
/// <summary>
/// Event fired when an ACL entry is stored or deleted.
/// </summary>
public event EventHandler<AclChangedEventArgs> AclChanged;
}
/// <summary>
/// Defines a delegate for a method that stores a ACL entry in the storage.
/// </summary>
/// <param name="entry">The entry to store.</param>
/// <returns><c>true</c> if the entry was stored, <c>false</c> otherwise.</returns>
public delegate bool StoreEntry(AclEntry entry);
/// <summary>
/// Defines a delegate for a method that deletes a ACL entry in the storage.
/// </summary>
/// <param name="entry">The entry to delete.</param>
/// <remarks><c>true</c> if the entry was deleted, <c>false</c> otherwise.</remarks>
public delegate bool DeleteEntry(AclEntry entry);
/// <summary>
/// Defines a delegate for a method that deletes ACL entries in the storage.
/// </summary>
/// <param name="entries">The entries to delete.</param>
/// <remarks><c>true</c> if one or more enties were deleted, <c>false</c> otherwise.</remarks>
public delegate bool DeleteEntries(AclEntry[] entries);
/// <summary>
/// Defines a delegate for a method that renames a 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 the resource was renamed, <c>false</c> otherwise.</returns>
public delegate bool RenameResource(string resource, string newName);
/// <summary>
/// Defines a delegate for a method that retrieves all entries.
/// </summary>
/// <returns>The entries.</returns>
public delegate AclEntry[] RetrieveAllEntries();
/// <summary>
/// Defines a delegate for a method that retrieves all entries for a resource.
/// </summary>
/// <param name="resource">The resource.</param>
/// <returns>The entries of the resource.</returns>
public delegate AclEntry[] RetrieveEntriesForResource(string resource);
/// <summary>
/// Defines a delegate for a method that retrieves all entries for a subject.
/// </summary>
/// <param name="subject">The subject.</param>
/// <returns>The entries of the subject.</returns>
public delegate AclEntry[] RetrieveEntriesForSubject(string subject);
}

View file

@ -1,82 +0,0 @@

using System;
using System.Collections.Generic;
using System.Text;
using ScrewTurn.Wiki.AclEngine;
using System.Data.Common;
namespace ScrewTurn.Wiki.Plugins.SqlCommon {
/// <summary>
/// Implements a SQL ACL Storer.
/// </summary>
public class SqlAclStorer : AclStorerBase {
private LoadData loadData;
private DeleteEntries deleteEntries;
private StoreEntries storeEntries;
/// <summary>
/// Initializes a new instance of the <see cref="T:SqlAclStorer" /> class.
/// </summary>
/// <param name="aclManager">The instance of the ACL Manager to handle.</param>
/// <param name="loadData">The <see cref="T:LoadData" /> delegate.</param>
/// <param name="deleteEntries">The <see cref="T:DeleteEntries" /> delegate.</param>
/// <param name="storeEntries">The <see cref="T:StoreEntries" /> delegate.</param>
public SqlAclStorer(IAclManager aclManager, LoadData loadData, DeleteEntries deleteEntries, StoreEntries storeEntries)
: base(aclManager) {
if(loadData == null) throw new ArgumentNullException("loadData");
if(deleteEntries == null) throw new ArgumentNullException("deleteEntries");
if(storeEntries == null) throw new ArgumentNullException("storeEntries");
this.loadData = loadData;
this.deleteEntries = deleteEntries;
this.storeEntries = storeEntries;
}
/// <summary>
/// Loads data from storage.
/// </summary>
/// <returns>The loaded ACL entries.</returns>
protected override AclEntry[] LoadDataInternal() {
return loadData();
}
/// <summary>
/// Deletes some entries.
/// </summary>
/// <param name="entries">The entries to delete.</param>
protected override void DeleteEntries(AclEntry[] entries) {
deleteEntries(entries);
}
/// <summary>
/// Stores some entries.
/// </summary>
/// <param name="entries">The entries to store.</param>
protected override void StoreEntries(AclEntry[] entries) {
storeEntries(entries);
}
}
/// <summary>
/// Defines a delegate for a method that loads ACL data from storage.
/// </summary>
/// <returns>The loaded ACL entries.</returns>
public delegate AclEntry[] LoadData();
/// <summary>
/// Defines a delegate for a method that deletes ACL entries in the storage.
/// </summary>
/// <param name="entries">The entries to delete.</param>
public delegate void DeleteEntries(AclEntry[] entries);
/// <summary>
/// Defines a delegate for a method that stores ACL entries in the storage.
/// </summary>
/// <param name="entries">The entries to store.</param>
public delegate void StoreEntries(AclEntry[] entries);
}

View file

@ -48,7 +48,7 @@
<Link>AssemblyVersion.cs</Link>
</Compile>
<Compile Include="IIndexConnector.cs" />
<Compile Include="SqlAclStorer.cs" />
<Compile Include="SqlAclManager.cs" />
<Compile Include="SqlClassBase.cs" />
<Compile Include="SqlFilesStorageProviderBase.cs" />
<Compile Include="SqlIndex.cs" />

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
}
}

View file

@ -65,8 +65,8 @@
<Compile Include="..\SqlProvidersCommon\QueryBuilder.cs">
<Link>SqlProvidersCommon\QueryBuilder.cs</Link>
</Compile>
<Compile Include="..\SqlProvidersCommon\SqlAclStorer.cs">
<Link>SqlProvidersCommon\SqlAclStorer.cs</Link>
<Compile Include="..\SqlProvidersCommon\SqlAclManager.cs">
<Link>SqlProvidersCommon\SqlAclManager.cs</Link>
</Compile>
<Compile Include="..\SqlProvidersCommon\SqlClassBase.cs">
<Link>SqlProvidersCommon\SqlClassBase.cs</Link>

View file

@ -13,7 +13,7 @@ namespace ScrewTurn.Wiki.Plugins.SqlServer {
/// </summary>
public class SqlServerSettingsStorageProvider : SqlSettingsStorageProviderBase {
private readonly ComponentInformation info = new ComponentInformation("SQL Server Settings Storage Provider", "ScrewTurn Software", "3.0.0.441", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/SQLServerProv/Settings.txt");
private readonly ComponentInformation info = new ComponentInformation("SQL Server Settings Storage Provider", "ScrewTurn Software", "3.0.1.446", "http://www.screwturn.eu", "http://www.screwturn.eu/Version/SQLServerProv/Settings.txt");
private readonly SqlServerCommandBuilder commandBuilder = new SqlServerCommandBuilder();

View file

@ -579,7 +579,21 @@ namespace ScrewTurn.Wiki.Tests {
prov = GetProvider();
Collectors.SettingsProvider = prov;
prov.AclManager.DeleteEntry("Res", "Action", "U.User");
Assert.IsTrue(prov.AclManager.RenameResource("Res", "NewName"), "RenameResource should return true");
entries = prov.AclManager.RetrieveAllEntries();
Assert.AreEqual(1, entries.Length, "Wrong entry count");
Assert.AreEqual("NewName", entries[0].Resource, "Wrong resource");
Assert.AreEqual("Action", entries[0].Action, "Wrong action");
Assert.AreEqual("U.User", entries[0].Subject, "Wrong subject");
Assert.AreEqual(Value.Grant, entries[0].Value, "Wrong value");
prov = null;
prov = GetProvider();
Collectors.SettingsProvider = prov;
Assert.IsTrue(prov.AclManager.DeleteEntry("NewName", "Action", "U.User"), "DeleteEntry should return true");
prov = null;
prov = GetProvider();