using System; using System.Collections.Generic; using System.Text; namespace ScrewTurn.Wiki.AclEngine { /// /// Implements a base class for an ACL Manager. /// /// All instance and static members are thread-safe. public abstract class AclManagerBase : IAclManager { private List entries; /// /// Initializes a new instance of the abstract class. /// public AclManagerBase() { entries = new List(100); } /// /// Handles the invokation of event. /// /// The changed entries. /// The change. protected void OnAclChanged(AclEntry[] entries, Change change) { if(AclChanged != null) { AclChanged(this, new AclChangedEventArgs(entries, change)); } } /// /// Stores a new ACL entry. /// /// The controlled resource. /// The action on the controlled resource. /// The subject whose access to the resource/action is controlled. /// The value of the entry. /// true if the entry is stored, false otherwise. /// If , or are null. /// If , or are empty. 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 result = new AclEntry(resource, action, subject, value); lock(this) { int index = entries.FindIndex(delegate(AclEntry x) { return AclEntry.Equals(x, result); }); if(index >= 0) { AclEntry removed = entries[index]; entries.RemoveAt(index); OnAclChanged(new AclEntry[] { removed }, Change.EntryDeleted); } entries.Add(result); OnAclChanged(new AclEntry[] { result }, Change.EntryStored); } return true; } /// /// Deletes an ACL entry. /// /// The controlled resource. /// The action on the controlled resource. /// The subject whose access to the resource/action is controlled. /// true if the entry is deleted, false otherwise. /// If , or are null. /// If , or are empty. 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 result = new AclEntry(resource, action, subject, Value.Deny); lock(this) { int index = entries.FindIndex(delegate(AclEntry x) { return AclEntry.Equals(x, result); }); if(index >= 0) { AclEntry entry = entries[index]; entries.RemoveAt(index); OnAclChanged(new AclEntry[] { entry }, Change.EntryDeleted); return true; } else return false; } } /// /// Deletes all the ACL entries for a resource. /// /// The controlled resource. /// true if the entries are deleted, false otherwise. /// If is null. /// If is empty. public bool DeleteEntriesForResource(string resource) { if(resource == null) throw new ArgumentNullException("resource"); if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource"); lock(this) { List indexesToRemove = new List(30); List entriesToRemove = new List(30); for(int i = 0; i < entries.Count; i++) { if(entries[i].Resource == resource) { indexesToRemove.Add(i); entriesToRemove.Add(entries[i]); } } if(indexesToRemove.Count > 0) { // Work in opposite direction to preserve smaller indexes for(int i = indexesToRemove.Count - 1; i >= 0; i--) { entries.RemoveAt(indexesToRemove[i]); } OnAclChanged(entriesToRemove.ToArray(), Change.EntryDeleted); return true; } else return false; } } /// /// Deletes all the ACL entries for a subject. /// /// The subject. /// true if the entries are deleted, false otherwise. /// If is null. /// If is empty. public bool DeleteEntriesForSubject(string subject) { if(subject == null) throw new ArgumentNullException("subject"); if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject"); lock(this) { List indexesToRemove = new List(30); List entriesToRemove = new List(30); for(int i = 0; i < entries.Count; i++) { if(entries[i].Subject == subject) { indexesToRemove.Add(i); entriesToRemove.Add(entries[i]); } } if(indexesToRemove.Count > 0) { // Work in opposite direction to preserve smaller indexes for(int i = indexesToRemove.Count - 1; i >= 0; i--) { entries.RemoveAt(indexesToRemove[i]); } OnAclChanged(entriesToRemove.ToArray(), Change.EntryDeleted); return true; } else return false; } } /// /// Renames a resource. /// /// The resource. /// The new name of the resource. /// true if the resource is renamed, false otherwise. /// If or are null. /// If or are empty. 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"); lock(this) { AclEntry[] entries = RetrieveEntriesForResource(resource); bool renamed = false; foreach(AclEntry entry in entries) { bool deleted = DeleteEntry(entry.Resource, entry.Action, entry.Subject); if(deleted) { bool stored = StoreEntry(newName, entry.Action, entry.Subject, entry.Value); if(stored) renamed = true; else return false; } else return false; } return renamed; } } /// /// Retrieves all the ACL entries for a resource. /// /// The entries. public AclEntry[] RetrieveAllEntries() { lock(this) { return entries.ToArray(); } } /// /// Retrieves all the ACL entries for a resource. /// /// The resource. /// The entries. /// If is null. /// If is empty. public AclEntry[] RetrieveEntriesForResource(string resource) { if(resource == null) throw new ArgumentNullException("resource"); if(resource.Length == 0) throw new ArgumentException("Resource cannot be empty", "resource"); lock(this) { List result = new List(10); foreach(AclEntry e in entries) { if(e.Resource == resource) result.Add(e); } return result.ToArray(); } } /// /// Retrieves all the ACL entries for a subject. /// /// The subject. /// The entries. /// If is null. /// If is empty. public AclEntry[] RetrieveEntriesForSubject(string subject) { if(subject == null) throw new ArgumentNullException("subject"); if(subject.Length == 0) throw new ArgumentException("Subject cannot be empty", "subject"); lock(this) { List result = new List(10); foreach(AclEntry e in entries) { if(e.Subject == subject) result.Add(e); } return result.ToArray(); } } /// /// Initializes the manager data. /// /// The ACL entries. /// If is null. public void InitializeData(AclEntry[] entries) { if(entries == null) throw new ArgumentNullException("entries"); lock(this) { this.entries = new List(entries); } } /// /// Gets the total number of ACL entries. /// public int TotalEntries { get { lock(this) { return entries.Count; } } } /// /// Event fired when an ACL entry is stored or deleted. /// public event EventHandler AclChanged; } }