diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/HasMasterAuthoritiesAttribute.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/HasMasterAuthoritiesAttribute.cs new file mode 100644 index 0000000..7203b0b --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/HasMasterAuthoritiesAttribute.cs @@ -0,0 +1,18 @@ +namespace Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +[AttributeUsage(AttributeTargets.Field)] +public class HasMasterAuthoritiesAttribute : Attribute +{ + public IReadOnlyList Authorities { get; init; } = []; + public bool AllAuthorities { get; init; } + + public HasMasterAuthoritiesAttribute(params string[] authorities) + { + Authorities = authorities; + } + + public HasMasterAuthoritiesAttribute(bool allAuthorities) + { + AllAuthorities = allAuthorities; + } +} \ No newline at end of file diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/HasMasterRolesAttribute.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/HasMasterRolesAttribute.cs new file mode 100644 index 0000000..15013fa --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/HasMasterRolesAttribute.cs @@ -0,0 +1,18 @@ +namespace Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +[AttributeUsage(AttributeTargets.Field)] +public class HasMasterRolesAttribute : Attribute +{ + public IReadOnlyList Roles { get; init; } = []; + public bool AllRoles { get; init; } + + public HasMasterRolesAttribute(params string[] roles) + { + Roles = roles; + } + + public HasMasterRolesAttribute(bool allRoles) + { + AllRoles = allRoles; + } +} \ No newline at end of file diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterAuthorityAttribute.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterAuthorityAttribute.cs new file mode 100644 index 0000000..007cea5 --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterAuthorityAttribute.cs @@ -0,0 +1,14 @@ +namespace Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +[AttributeUsage(AttributeTargets.Field)] +public class MasterAuthorityAttribute : Attribute +{ + public string? OldKey { get; init; } + + public MasterAuthorityAttribute() {} + + public MasterAuthorityAttribute(string? oldKey) + { + OldKey = oldKey; + } +} \ No newline at end of file diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterGroupAttribute.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterGroupAttribute.cs new file mode 100644 index 0000000..234d0a3 --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterGroupAttribute.cs @@ -0,0 +1,14 @@ +namespace Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +[AttributeUsage(AttributeTargets.Field)] +public class MasterGroupAttribute : Attribute +{ + public string? OldKey { get; init; } + + public MasterGroupAttribute() {} + + public MasterGroupAttribute(string? oldKey) + { + OldKey = oldKey; + } +} \ No newline at end of file diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterRoleAttribute.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterRoleAttribute.cs new file mode 100644 index 0000000..777286e --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/Atttibutes/MasterRoleAttribute.cs @@ -0,0 +1,14 @@ +namespace Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +[AttributeUsage(AttributeTargets.Field)] +public class MasterRoleAttribute : Attribute +{ + public string? OldKey { get; init; } + + public MasterRoleAttribute() {} + + public MasterRoleAttribute(string? oldKey) + { + OldKey = oldKey; + } +} \ No newline at end of file diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterAuthorities.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterAuthorities.cs new file mode 100644 index 0000000..7bad799 --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterAuthorities.cs @@ -0,0 +1,59 @@ +using System.ComponentModel; +using System.Reflection; +using Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +namespace Suspectus.Gandalf.Palantir.Data.MasterData; + +public static class MasterAuthorities +{ + public static class Profile + { + [MasterAuthority] [Description("Allows to view any profile.")] + public const string Read = "g.profile.read"; + + [MasterAuthority] [Description("Allows to edit any profile.")] + public const string Write = "g.profile.write"; + + public static class Self + { + [MasterAuthority] [Description("Allows to view your own profile.")] + public const string Read = "g.profile.self.read"; + + [MasterAuthority] [Description("Allows to edit your own profile.")] + public const string Write = "g.profile.self.write"; + } + } + + public static List GetAllAuthorities() + { + var authorities = new List(); + CollectFields(typeof(MasterAuthorities)); + + return authorities + .Select(x => new MasterAuthorityData + { + Name = (string)x.GetValue(null)!, + OldName = x.GetCustomAttribute()!.OldKey, + Description = x.GetCustomAttribute()?.Description ?? null + }).ToList(); + + void CollectFields(Type type) + { + var fields = type + .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) + .Where(f => f.FieldType == typeof(string) && Attribute.IsDefined(f, typeof(MasterAuthorityAttribute))); + + authorities.AddRange(fields); + + foreach (var nested in type.GetNestedTypes()) + CollectFields(nested); + } + } + + public class MasterAuthorityData + { + public string Name { get; set; } = null!; + public string? OldName { get; set; } + public string? Description { get; set; } + } +} \ No newline at end of file diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterGroups.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterGroups.cs new file mode 100644 index 0000000..b3e4a41 --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterGroups.cs @@ -0,0 +1,61 @@ +using System.ComponentModel; +using System.Reflection; +using Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +namespace Suspectus.Gandalf.Palantir.Data.MasterData; + +public static class MasterGroups +{ + [MasterGroup] + [Description("Contains the minimal set of Roles to use the application.")] + [HasMasterRoles(MasterRoles.SelfProfile)] + public const string BasicUser = "Basic User"; + + public static List GetAllGroups() + { + var groups = new List(); + CollectFields(typeof(MasterGroups)); + + return groups + .Select(x => new MasterGroupData + { + Name = (string)x.GetValue(null)!, + OldName = x.GetCustomAttribute()!.OldKey, + Description = x.GetCustomAttribute()?.Description ?? null, + Roles = GetRoles(x) + }).ToList(); + + IReadOnlyList GetRoles(FieldInfo field) + { + var hasMasterRolesAttribute = field.GetCustomAttribute(); + + if (hasMasterRolesAttribute is null) + return []; + + if (hasMasterRolesAttribute.AllRoles) + return MasterRoles.GetAllRoles().Select(x => x.Name).ToList(); + + return hasMasterRolesAttribute.Roles; + } + + void CollectFields(Type type) + { + var fields = type + .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) + .Where(f => f.FieldType == typeof(string) && Attribute.IsDefined(f, typeof(MasterGroupAttribute))); + + groups.AddRange(fields); + + foreach (var nested in type.GetNestedTypes()) + CollectFields(nested); + } + } + + public class MasterGroupData + { + public required string Name { get; set; } + public string? OldName { get; set; } + public string? Description { get; set; } + public IReadOnlyList Roles { get; set; } = []; + } +} \ No newline at end of file diff --git a/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterRoles.cs b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterRoles.cs new file mode 100644 index 0000000..abc390e --- /dev/null +++ b/src/dotnet/Suspectus.Gandalf.Palantir.Data/MasterData/MasterRoles.cs @@ -0,0 +1,66 @@ +using System.ComponentModel; +using System.Reflection; +using Suspectus.Gandalf.Palantir.Data.MasterData.Atttibutes; + +namespace Suspectus.Gandalf.Palantir.Data.MasterData; + +public static class MasterRoles +{ + [MasterRole] + [Description("Allows everything.")] + [HasMasterAuthorities(true)] + public const string Housemaster = nameof(Housemaster); + + [MasterRole] + [Description("Allows to view your own profile.")] + [HasMasterAuthorities(MasterAuthorities.Profile.Self.Read, MasterAuthorities.Profile.Self.Write)] + public const string SelfProfile = "Self Profile"; + + public static List GetAllRoles() + { + var roles = new List(); + CollectFields(typeof(MasterRoles)); + + return roles + .Select(x => new MasterRoleData + { + Name = (string)x.GetValue(null)!, + OldName = x.GetCustomAttribute()!.OldKey, + Description = x.GetCustomAttribute()?.Description ?? null, + Authorities = GetAuthorities(x) + }).ToList(); + + IReadOnlyList GetAuthorities(FieldInfo field) + { + var hasMasterAuthoritiesAttribute = field.GetCustomAttribute(); + + if (hasMasterAuthoritiesAttribute is null) + return []; + + if (hasMasterAuthoritiesAttribute.AllAuthorities) + return MasterAuthorities.GetAllAuthorities().Select(x => x.Name).ToList(); + + return hasMasterAuthoritiesAttribute.Authorities; + } + + void CollectFields(Type type) + { + var fields = type + .GetFields(BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy) + .Where(f => f.FieldType == typeof(string) && Attribute.IsDefined(f, typeof(MasterRoleAttribute))); + + roles.AddRange(fields); + + foreach (var nested in type.GetNestedTypes()) + CollectFields(nested); + } + } + + public class MasterRoleData + { + public required string Name { get; set; } + public string? OldName { get; set; } + public string? Description { get; set; } + public IReadOnlyList Authorities { get; set; } = []; + } +} \ No newline at end of file