diff --git a/AssignedRole.cs b/AssignedRole.cs new file mode 100644 index 0000000..17b36ec --- /dev/null +++ b/AssignedRole.cs @@ -0,0 +1,33 @@ +using System; +namespace ln.identities +{ + public class AssignedRole + { + IIdentityProvider identityProvider; + public IIdentityProvider IdentityProvider => identityProvider; + + readonly Guid identityUniqueID; + public Identity Identity => IdentityProvider.GetIdentity(identityUniqueID); + + public Role Role { get; set; } + + protected AssignedRole(IIdentityProvider identityProvider) + { + this.identityProvider = identityProvider; + } + + public AssignedRole(IIdentityProvider identityProvider, Identity identity) : this(identityProvider, identity, 0) { } + public AssignedRole(IIdentityProvider identityProvider,Identity identity,Role role) :this(identityProvider) + { + identityUniqueID = identity.UniqueID; + Role = role; + } + + public override string ToString() => String.Format("[AssignedRole Identity={0} Role={1}]",Identity.IdentityName,Role); + + public override int GetHashCode() => identityUniqueID.GetHashCode(); + public override bool Equals(object obj) => (obj is AssignedRole you) && (identityUniqueID.Equals(you.identityUniqueID)); + + + } +} diff --git a/AssignedRoles.cs b/AssignedRoles.cs new file mode 100644 index 0000000..e5e3d15 --- /dev/null +++ b/AssignedRoles.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections; +using System.Collections.Generic; +namespace ln.identities +{ + public class AssignedRoles : IEnumerable + { + IIdentityProvider identityProvider; + public IIdentityProvider IdentityProvider => identityProvider; + + Dictionary assignedRoles = new Dictionary(); + + public AssignedRoles(IIdentityProvider identityProvider) + { + this.identityProvider = identityProvider; + } + + public AssignedRole this[Identity identity] + { + get + { + if (!assignedRoles.ContainsKey(identity.UniqueID)) + { + assignedRoles.Add(identity.UniqueID, new AssignedRole(identityProvider, identity)); + } + return assignedRoles[identity.UniqueID]; + } + } + + public IEnumerator GetEnumerator() + { + return assignedRoles.Values.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} diff --git a/BaseIdentityProvider.cs b/BaseIdentityProvider.cs index ab87e9f..8c107c7 100644 --- a/BaseIdentityProvider.cs +++ b/BaseIdentityProvider.cs @@ -30,5 +30,44 @@ namespace ln.identities public abstract bool Save(Identity identity); public virtual Identity GetIdentity(string identityName) => GetIdentity(GetIdentities().FirstOrDefault((kvp) => identityName.Equals(kvp.Value)).Key); + + public abstract IEnumerable GetRoleAssignments(Identity identity); + + public virtual AssignedRoles GetAssignedRoles(Identity identity) + { + AssignedRoles assignedRoles = new AssignedRoles(this); + GetAssignedRoles(identity, assignedRoles); + return assignedRoles; + } + + public virtual void GetAssignedRoles(Identity identity,AssignedRoles assignedRoles) + { + assignedRoles[identity].Role |= Role.BE; + + foreach (RoleAssignment roleAssignment in identity.RoleAssignments) + { + foreach (AssignedRole foreignRole in roleAssignment.EffectiveIdentity.AssignedRoles) + { + if (roleAssignment.Role.HasFlag(Role.IMPERSONATE) || foreignRole.Identity.Equals(roleAssignment.EffectiveIdentity)) + { + AssignedRole currentRole = assignedRoles[foreignRole.Identity]; + currentRole.Role |= (foreignRole.Role & roleAssignment.Role); + } + } + } + } + + + + + + + + + + + + + } } diff --git a/IIdentityProvider.cs b/IIdentityProvider.cs index 17da2ba..9fe8439 100644 --- a/IIdentityProvider.cs +++ b/IIdentityProvider.cs @@ -14,5 +14,8 @@ namespace ln.identities bool Save(Identity identity); Identity Authenticate(AuthenticationProve authenticationProve); + + AssignedRoles GetAssignedRoles(Identity identity); + IEnumerable GetRoleAssignments(Identity identity); } } diff --git a/Identity.cs b/Identity.cs index 7bab4d7..b46b4ad 100644 --- a/Identity.cs +++ b/Identity.cs @@ -6,24 +6,37 @@ namespace ln.identities { public class Identity { - public Guid UniqueID { get; private set; } + private IIdentityProvider identityProvider; + public IIdentityProvider IdentityProvider => identityProvider; + public Guid UniqueID { get; private set; } public String IdentityName { get; set; } List secureAttributes = new List(); - List roleAssignments = new List(); + AssignedRoles assignedRoles; - private Identity() + List cachedRoleAssignments; + + private Identity(IIdentityProvider identityProvider) { + this.identityProvider = identityProvider; } - private Identity(Guid uniqueID,string identityName) + private Identity(IIdentityProvider identityProvider,Guid uniqueID,string identityName) + :this(identityProvider) { UniqueID = uniqueID; IdentityName = identityName; } - public Identity(string identityName) : this(Guid.NewGuid(),identityName){} + public Identity(IIdentityProvider identityProvider,string identityName) : this(identityProvider,Guid.NewGuid(),identityName){} + + public void ResetCaches() + { + cachedRoleAssignments = null; + } + + public SecureAttribute GetSecureAttribute(Guid uniqueID) { @@ -54,8 +67,33 @@ namespace ln.identities public void AddSecureAttribute(SecureAttribute secureAttribute) => secureAttributes.Add(secureAttribute); public void RemoveSecureAttribute(SecureAttribute secureAttribute) => secureAttributes.Remove(secureAttribute); + public void ClearAssignedRolesCache() => assignedRoles = null; + public AssignedRoles AssignedRoles + { + get { + if (assignedRoles == null) + { + assignedRoles = IdentityProvider.GetAssignedRoles(this); + } + return assignedRoles; + } + } + + public IEnumerable RoleAssignments + { + get + { + if (cachedRoleAssignments == null) + { + cachedRoleAssignments = new List(IdentityProvider.GetRoleAssignments(this)); + } + return cachedRoleAssignments; + } + } + public override bool Equals(object obj) => (obj is Identity other) && other.UniqueID.Equals(UniqueID); public override int GetHashCode() => UniqueID.GetHashCode(); + public override string ToString() => String.Format("[Identity UniqueID={0} IdentityName={1}]",UniqueID,IdentityName); } } diff --git a/ODBIdentityProvider.cs b/ODBIdentityProvider.cs index 9fc81e1..d9a85fd 100644 --- a/ODBIdentityProvider.cs +++ b/ODBIdentityProvider.cs @@ -6,6 +6,8 @@ using System.Linq; using ln.types.btree; using ln.types.collections; using System.Security.Principal; +using ln.types.odb.ng.mappings; +using System.Reflection; namespace ln.identities { public class ODBIdentityProvider : BaseIdentityProvider @@ -20,6 +22,13 @@ namespace ln.identities StorageContainer = storageContainer; mapper = new Mapper(storageContainer); + ClassMapping identityClassMapping = new ClassMapping( + typeof(Identity), + (Mapper arg1, Document arg2) => new Identity(this,""), + (fieldInfo) => !fieldInfo.Name.Equals("identityProvider") + ); + mapper.RegisterMapping(identityClassMapping.MappedType,identityClassMapping); + mapper.EnsureIndex("UniqueID"); mapper.EnsureIndex("IdentityName"); } @@ -54,7 +63,7 @@ namespace ln.identities public override Identity CreateIdentity(string identityName) { - Identity identity = new Identity(identityName); + Identity identity = new Identity(this,identityName); identityCache.Add(identity.UniqueID, identity); return identity; } @@ -74,5 +83,9 @@ namespace ln.identities return true; } + public override IEnumerable GetRoleAssignments(Identity identity) + { + throw new NotImplementedException(); + } } } diff --git a/Role.cs b/Role.cs index 8be466a..f789983 100644 --- a/Role.cs +++ b/Role.cs @@ -1,27 +1,51 @@ using System; namespace ln.identities { - public class Role + [Flags] + public enum Role : int { - public static readonly Role SuperUser = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000000}"),"SuperUser"); - public static readonly Role Owner = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000001}"), "Owner"); - public static readonly Role Editor = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000002}"), "Editor"); + /* Bits 0..15: Rolleneigenschaften */ + VIEW = (1<<0), // Authentifizierte Identität darf die effektive Identität und ihre Eigenschaften sehen + USE = VIEW | (1 << 1), // Authentifizierte Identität darf die effektive Identität nutzen (z.B. Dateien einer Webpräsenz pflegen) + CONTROL = VIEW | (1<<2), // Authentifizierte Identität darf die effektive Identität steuern (z.B. einen Dienst starten / stoppen) + MANAGE = VIEW | CONTROL | (1<<3), // Authentifizierte Identität darf die Eigenschaften der effektiven Identität verändern ("Konfiguration") - public Guid UniqueID { get; } - public String Name { get; set; } + ADMIN = 0x0000FFFF, + /* Bits 16..23: */ + MANAGEROLES = (1<<16), - private Role() - {} + /* Bits 24..31: Vererbung, Superuser */ + IMPERSONATE = (1<<24), // FLAG: Identität erhält alle maskierten Rollen der effektiven Identität, welche direkt assoziiert sind - public Role(string roleName):this(Guid.NewGuid(),roleName){} - public Role(Guid uniqueID,string roleName) - { - UniqueID = uniqueID; - Name = roleName; - } - - public override bool Equals(object obj) => (obj is Role other) && other.UniqueID.Equals(UniqueID); - public override int GetHashCode() => UniqueID.GetHashCode(); + OWN = 0x0FFFFFFF, + BE = 0x0000FFFF, + SUPER = 0x7FFFFFFF, // SuperUser } + //public class Role + //{ + // public static readonly Role SuperUser = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000000}"),"SuperUser"); + // public static readonly Role Owner = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000001}"), "Owner"); + // public static readonly Role Editor = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000002}"), "Editor"); + // public static readonly Role Reader = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000004}"), "Reader"); + // public static readonly Role Writer = new Role(Guid.Parse("{eefca5e2-2295-44d5-9b24-000000000008}"), "Writer"); + + + // public Guid UniqueID { get; } + // public String Name { get; set; } + + // private Role() + // {} + + // public Role(string roleName):this(Guid.NewGuid(),roleName){} + // public Role(Guid uniqueID,string roleName) + // { + // UniqueID = uniqueID; + // Name = roleName; + // } + + // public override bool Equals(object obj) => (obj is Role other) && other.UniqueID.Equals(UniqueID); + // public override int GetHashCode() => UniqueID.GetHashCode(); + + //} } diff --git a/RoleAssignment.cs b/RoleAssignment.cs index ed70884..0719111 100644 --- a/RoleAssignment.cs +++ b/RoleAssignment.cs @@ -3,11 +3,44 @@ namespace ln.identities { public class RoleAssignment { - public Identity EffectiveIdentity { get; } - public Role Role { get; } + IIdentityProvider identityProvider; + public IIdentityProvider IdentityProvider => identityProvider; - private RoleAssignment() + Guid identityUniqueID; + Guid effectiveIdentityUniqueID; + + public Identity Identity => IdentityProvider.GetIdentity(identityUniqueID); // Identity this Role is assigned to + public Identity EffectiveIdentity => IdentityProvider.GetIdentity(effectiveIdentityUniqueID); // Identity for that this Role is valid + public Role Role { get; } // Role that is assigned + + private RoleAssignment(IIdentityProvider identityProvider) { + this.identityProvider = identityProvider; } + + public RoleAssignment(IIdentityProvider identityProvider,Identity identity,Identity effectiveIdentity,Role role) + :this(identityProvider) + { + identityUniqueID = identity.UniqueID; + effectiveIdentityUniqueID = effectiveIdentity.UniqueID; + Role = role; + } + + public override int GetHashCode() + { + return EffectiveIdentity.GetHashCode() ^ Role.GetHashCode(); + } + + public override bool Equals(object obj) + { + return (obj is RoleAssignment you) && + (identityUniqueID.Equals(you.identityUniqueID) && effectiveIdentityUniqueID.Equals(you.effectiveIdentityUniqueID) && Role.Equals(you.Role)); + } + + public override string ToString() + { + return string.Format("[RoleAssigment Identity={0} EffectiveIdentity={1} Role={2}]",Identity.IdentityName,EffectiveIdentity.IdentityName,Role); + } + } } diff --git a/SeededPassword.cs b/SeededPassword.cs index f044fcb..d655b52 100644 --- a/SeededPassword.cs +++ b/SeededPassword.cs @@ -28,15 +28,12 @@ namespace ln.identities using (SHA256 sha256 = SHA256.Create()) { byte[] passwordBytes = Encoding.UTF8.GetBytes(password); - Console.WriteLine("PasswordBytes={0}", passwordBytes.ToHexString()); - Console.WriteLine("Seed={0}", Seed.ToHexString()); sha256.TransformBlock(Seed, 0, Seed.Length, null, 0); sha256.TransformBlock(passwordBytes, 0, passwordBytes.Length, null, 0); sha256.TransformFinalBlock(Seed, 0, Seed.Length); secretBytes = sha256.Hash; - Console.WriteLine("SecretBytes={0}", secretBytes.ToHexString()); } } diff --git a/ln.identities.csproj b/ln.identities.csproj index 5ec3d49..6edeae5 100644 --- a/ln.identities.csproj +++ b/ln.identities.csproj @@ -28,6 +28,9 @@ + + nunit + @@ -42,6 +45,9 @@ + + + @@ -49,5 +55,8 @@ ln.types + + + \ No newline at end of file diff --git a/test/IdentityTests.cs b/test/IdentityTests.cs new file mode 100644 index 0000000..408380b --- /dev/null +++ b/test/IdentityTests.cs @@ -0,0 +1,88 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ln.identities.test +{ + + public class TestIdentityProvider : BaseIdentityProvider + { + List identities = new List(); + List roleAssignments = new List(); + + public TestIdentityProvider() + { + } + + public override Identity CreateIdentity(string identityName) + { + Identity identity = new Identity(this, identityName); + identities.Add(identity); + return identity; + } + + public override IEnumerable> GetIdentities() => identities.Select((Identity arg) => new KeyValuePair(arg.UniqueID, arg.IdentityName)); + public override Identity GetIdentity(Guid uniqueID) + { + foreach (Identity identity in identities) + if (identity.UniqueID.Equals(uniqueID)) + return identity; + throw new KeyNotFoundException(); + } + public override IEnumerable GetRoleAssignments(Identity identity) => roleAssignments.Where((RoleAssignment arg) => arg.Identity.Equals(identity)); + public override bool Save(Identity identity) + { + throw new NotImplementedException(); + } + + public void AddRoleAssignment(RoleAssignment roleAssignment) => roleAssignments.Add(roleAssignment); + } + + [TestFixture()] + public class IdentityTests + { + [Test()] + public void TestCase() + { + TestIdentityProvider identityProvider = new TestIdentityProvider(); + + Identity idService = identityProvider.CreateIdentity("Service"); + Identity idOrg = identityProvider.CreateIdentity("Organization"); + Identity idManager = identityProvider.CreateIdentity("Manager"); + Identity idEmpA = identityProvider.CreateIdentity("EmployeeA"); + Identity idEmpB = identityProvider.CreateIdentity("EmployeeB"); + Identity idEmpC = identityProvider.CreateIdentity("EmployeeC"); + Identity idEmpD = identityProvider.CreateIdentity("EmployeeD"); + + identityProvider.AddRoleAssignment(new RoleAssignment(identityProvider,idOrg,idService,Role.OWN)); + identityProvider.AddRoleAssignment(new RoleAssignment(identityProvider, idManager, idOrg, Role.OWN)); + identityProvider.AddRoleAssignment(new RoleAssignment(identityProvider, idEmpA, idOrg, Role.IMPERSONATE | Role.CONTROL)); + identityProvider.AddRoleAssignment(new RoleAssignment(identityProvider, idEmpB, idEmpA, Role.IMPERSONATE | Role.ADMIN)); + identityProvider.AddRoleAssignment(new RoleAssignment(identityProvider, idEmpC, idOrg, Role.IMPERSONATE | Role.CONTROL)); + identityProvider.AddRoleAssignment(new RoleAssignment(identityProvider, idEmpD, idEmpC, Role.ADMIN)); + + DumpRoleAssigments(idService); + DumpRoleAssigments(idOrg); + DumpRoleAssigments(idManager); + DumpRoleAssigments(idEmpA); + DumpRoleAssigments(idEmpB); + DumpRoleAssigments(idEmpC); + DumpRoleAssigments(idEmpD); + } + + public void DumpRoleAssigments(Identity identity) + { + Console.WriteLine("Identity: {0}", identity.IdentityName); + foreach (RoleAssignment roleAssignment in identity.RoleAssignments) + { + Console.WriteLine(" {0}", roleAssignment); + } + foreach (AssignedRole assignedRole in identity.AssignedRoles) + { + Console.WriteLine(" {0}", assignedRole); + } + } + + } +}