Module: Organizations::Roles

Defined in:
lib/organizations/roles.rb

Overview

Roles and permissions management module. Provides hierarchical roles with inherited permissions.

The role hierarchy is: owner > admin > member > viewer Each role inherits all permissions from roles below it.

Permission lookups are O(1) using pre-computed permission sets.

Defined Under Namespace

Classes: RoleBuilder

Constant Summary collapse

HIERARCHY =

Role hierarchy from highest to lowest

%i[owner admin member viewer].freeze
DEFAULT_PERMISSIONS =

Default permissions for each role. Matches the permission table in the README.

{
  viewer: %i[
    view_organization
    view_members
  ].freeze,
  member: %i[
    view_organization
    view_members
    create_resources
    edit_own_resources
    delete_own_resources
  ].freeze,
  admin: %i[
    view_organization
    view_members
    create_resources
    edit_own_resources
    delete_own_resources
    invite_members
    remove_members
    edit_member_roles
    manage_settings
    view_billing
  ].freeze,
  owner: %i[
    view_organization
    view_members
    create_resources
    edit_own_resources
    delete_own_resources
    invite_members
    remove_members
    edit_member_roles
    manage_settings
    view_billing
    manage_billing
    transfer_ownership
    delete_organization
  ].freeze
}.freeze

Class Method Summary collapse

Class Method Details

.at_least?(role_a, role_b) ⇒ Boolean

Check if role_a is at least as high as role_b in hierarchy

Examples:

Roles.at_least?(:owner, :admin)  # => true (owner >= admin)
Roles.at_least?(:member, :admin) # => false (member < admin)

Parameters:

  • role_a (Symbol, String)

    The role to check

  • role_b (Symbol, String)

    The minimum required role

Returns:

  • (Boolean)


96
97
98
99
100
101
102
103
104
105
106
# File 'lib/organizations/roles.rb', line 96

def at_least?(role_a, role_b)
  return false if role_a.nil? || role_b.nil?

  idx_a = HIERARCHY.index(role_a.to_sym)
  idx_b = HIERARCHY.index(role_b.to_sym)

  return false unless idx_a && idx_b

  # Lower index = higher rank (owner is index 0)
  idx_a <= idx_b
end

.compare(role_a, role_b) ⇒ Integer

Compare two roles

Parameters:

  • role_a (Symbol, String)

    First role

  • role_b (Symbol, String)

    Second role

Returns:

  • (Integer)

    -1 if a > b, 0 if equal, 1 if a < b



112
113
114
115
116
117
118
119
# File 'lib/organizations/roles.rb', line 112

def compare(role_a, role_b)
  idx_a = HIERARCHY.index(role_a.to_sym)
  idx_b = HIERARCHY.index(role_b.to_sym)

  return 0 if idx_a == idx_b

  idx_a < idx_b ? -1 : 1
end

.defaultHash<Symbol, Array<Symbol>>

Get the default permission structure

Returns:

  • (Hash<Symbol, Array<Symbol>>)


62
63
64
# File 'lib/organizations/roles.rb', line 62

def default
  DEFAULT_PERMISSIONS
end

.has_permission?(role, permission) ⇒ Boolean

Check if a role has a specific permission (O(1) lookup)

Examples:

Roles.has_permission?(:admin, :invite_members) # => true
Roles.has_permission?(:member, :invite_members) # => false

Parameters:

  • role (Symbol, String)

    The role

  • permission (Symbol, String)

    The permission to check

Returns:

  • (Boolean)


140
141
142
143
144
# File 'lib/organizations/roles.rb', line 140

def has_permission?(role, permission)
  return false if role.nil? || permission.nil?

  permission_sets[role.to_sym]&.include?(permission.to_sym) || false
end

.hierarchyArray<Symbol>

Get the role hierarchy

Returns:

  • (Array<Symbol>)


68
69
70
# File 'lib/organizations/roles.rb', line 68

def hierarchy
  HIERARCHY
end

.higher_role(role) ⇒ Symbol?

Get the next role up in hierarchy

Parameters:

  • role (Symbol, String)

    Current role

Returns:

  • (Symbol, nil)

    Next higher role or nil if already highest



167
168
169
170
171
172
173
174
# File 'lib/organizations/roles.rb', line 167

def higher_role(role)
  return nil if role.nil?

  idx = HIERARCHY.index(role.to_sym)
  return nil unless idx && idx > 0

  HIERARCHY[idx - 1]
end

.lower_role(role) ⇒ Symbol?

Get the next role down in hierarchy

Parameters:

  • role (Symbol, String)

    Current role

Returns:

  • (Symbol, nil)

    Next lower role or nil if already lowest



179
180
181
182
183
184
185
186
# File 'lib/organizations/roles.rb', line 179

def lower_role(role)
  return nil if role.nil?

  idx = HIERARCHY.index(role.to_sym)
  return nil unless idx && idx < HIERARCHY.length - 1

  HIERARCHY[idx + 1]
end

.permission_setsHash<Symbol, Set<Symbol>>

Get the pre-computed permission sets (for O(1) lookups)

Returns:

  • (Hash<Symbol, Set<Symbol>>)


154
155
156
# File 'lib/organizations/roles.rb', line 154

def permission_sets
  @permission_sets ||= permissions.transform_values { |perms| Set.new(perms) }
end

.permissionsHash<Symbol, Array<Symbol>>

Get the pre-computed permission hash (for direct access)

Returns:

  • (Hash<Symbol, Array<Symbol>>)


148
149
150
# File 'lib/organizations/roles.rb', line 148

def permissions
  @permissions ||= compute_permissions
end

.permissions_for(role) ⇒ Array<Symbol>

Get all permissions for a role (pre-computed, O(1) lookup)

Parameters:

  • role (Symbol, String)

    The role

Returns:

  • (Array<Symbol>)

    All permissions for the role



124
125
126
127
128
129
# File 'lib/organizations/roles.rb', line 124

def permissions_for(role)
  return [] if role.nil?

  role_sym = role.to_sym
  permissions[role_sym] || []
end

.reset!Object

Reset computed permissions (used when custom roles are defined)



159
160
161
162
# File 'lib/organizations/roles.rb', line 159

def reset!
  @permissions = nil
  @permission_sets = nil
end

.valid_role?(role) ⇒ Boolean

Check if a role is valid

Parameters:

  • role (Symbol, String)

    The role to check

Returns:

  • (Boolean)


81
82
83
84
85
# File 'lib/organizations/roles.rb', line 81

def valid_role?(role)
  return false if role.nil?

  HIERARCHY.include?(role.to_sym)
end

.valid_rolesArray<Symbol>

Get all valid role names

Returns:

  • (Array<Symbol>)


74
75
76
# File 'lib/organizations/roles.rb', line 74

def valid_roles
  HIERARCHY
end