@@ -31,13 +31,14 @@ public static void Build(Assembly assembly)
3131 var discovered = Discover ( assembly ) ;
3232
3333 // Metadata order is stable per build but not contractual, so sort for a deterministic tree.
34- foreach ( var ( name , extraParents , isStaffOnly ) in discovered . OrderBy ( entry => entry . Name , StringComparer . Ordinal ) )
34+ foreach ( var ( name , extraParents , isStaffOnly , cascades ) in discovered . OrderBy ( entry => entry . Name , StringComparer . Ordinal ) )
3535 {
3636 Nodes [ name ] = new PermissionNode
3737 {
3838 Name = name ,
3939 ExtraParents = extraParents ,
4040 IsStaffOnly = isStaffOnly ,
41+ CascadesStaffOnly = cascades ,
4142 } ;
4243 }
4344
@@ -74,7 +75,7 @@ public static bool RegisterDynamic(string permission, string source, bool staffO
7475 {
7576 Name = permission ,
7677 Source = source ,
77- IsStaffOnly = parent . IsStaffOnly || staffOnly ,
78+ IsStaffOnly = ( parent . IsStaffOnly && parent . CascadesStaffOnly ) || staffOnly ,
7879 ExtraParents = parent . ExtraParents ,
7980 StructuralParent = parent ,
8081 } ;
@@ -204,9 +205,9 @@ private static Type[] TypesIn(Assembly assembly)
204205 }
205206 }
206207
207- private static List < ( string Name , string [ ] ExtraParents , bool IsStaffOnly ) > Discover ( Assembly assembly )
208+ private static List < ( string Name , string [ ] ExtraParents , bool IsStaffOnly , bool Cascades ) > Discover ( Assembly assembly )
208209 {
209- var discovered = new List < ( string Name , string [ ] ExtraParents , bool IsStaffOnly ) > ( ) ;
210+ var discovered = new List < ( string Name , string [ ] ExtraParents , bool IsStaffOnly , bool Cascades ) > ( ) ;
210211 var owners = new Dictionary < string , string > ( StringComparer . OrdinalIgnoreCase ) ;
211212
212213 foreach ( var type in TypesIn ( assembly ) )
@@ -219,7 +220,7 @@ private static Type[] TypesIn(Assembly assembly)
219220 }
220221
221222 var prefix = category . Prefix ?? DerivePrefix ( type ) ;
222- var categoryIsStaffOnly = type . GetCustomAttribute < StaffOnlyAttribute > ( ) is not null ;
223+ var categoryStaffOnly = type . GetCustomAttribute < StaffOnlyAttribute > ( ) ;
223224 var hasContainerGrant = false ;
224225 var declaredInCategory = 0 ;
225226
@@ -258,8 +259,8 @@ private static Type[] TypesIn(Assembly assembly)
258259 hasContainerGrant |= PermissionPath . IsContainerGrant ( value ) ;
259260 declaredInCategory ++ ;
260261
261- var isStaffOnly = categoryIsStaffOnly || field . GetCustomAttribute < StaffOnlyAttribute > ( ) is not null ;
262- discovered . Add ( ( value , category . AdditionalParents , isStaffOnly ) ) ;
262+ var staffOnly = field . GetCustomAttribute < StaffOnlyAttribute > ( ) ?? categoryStaffOnly ;
263+ discovered . Add ( ( value , category . AdditionalParents , staffOnly is not null , staffOnly ? . Cascades ?? true ) ) ;
263264 }
264265
265266 // A single-permission category has nothing to group, so no container grant is expected. Neither does
@@ -321,7 +322,7 @@ static void MarkStaffOnlyBelow(PermissionNode node)
321322 {
322323 foreach ( var child in node . StructuralChildren )
323324 {
324- child . IsStaffOnly |= node . IsStaffOnly ;
325+ child . IsStaffOnly |= node . IsStaffOnly && node . CascadesStaffOnly ;
325326 MarkStaffOnlyBelow ( child ) ;
326327 }
327328 }
0 commit comments