scimitar-rbac 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +170 -0
- data/Rakefile +8 -0
- data/lib/generators/scimitar_rbac/install_generator.rb +60 -0
- data/lib/generators/scimitar_rbac/templates/application_model.rb.erb +55 -0
- data/lib/generators/scimitar_rbac/templates/applications_controller.rb.erb +15 -0
- data/lib/generators/scimitar_rbac/templates/entitlement_model.rb.erb +114 -0
- data/lib/generators/scimitar_rbac/templates/entitlements_controller.rb.erb +15 -0
- data/lib/generators/scimitar_rbac/templates/migration.rb.erb +91 -0
- data/lib/generators/scimitar_rbac/templates/role_model.rb.erb +105 -0
- data/lib/generators/scimitar_rbac/templates/roles_controller.rb.erb +15 -0
- data/lib/scimitar/rbac/complex_types/application_reference.rb +14 -0
- data/lib/scimitar/rbac/complex_types/entitlement_assignment.rb +14 -0
- data/lib/scimitar/rbac/complex_types/hierarchy_member.rb +17 -0
- data/lib/scimitar/rbac/complex_types/role_assignment.rb +14 -0
- data/lib/scimitar/rbac/engine.rb +13 -0
- data/lib/scimitar/rbac/resources/application.rb +16 -0
- data/lib/scimitar/rbac/resources/entitlement.rb +16 -0
- data/lib/scimitar/rbac/resources/role.rb +16 -0
- data/lib/scimitar/rbac/route_helper.rb +65 -0
- data/lib/scimitar/rbac/schema/application.rb +36 -0
- data/lib/scimitar/rbac/schema/application_reference.rb +18 -0
- data/lib/scimitar/rbac/schema/entitlement.rb +50 -0
- data/lib/scimitar/rbac/schema/entitlement_assignment.rb +20 -0
- data/lib/scimitar/rbac/schema/hierarchy_member.rb +19 -0
- data/lib/scimitar/rbac/schema/role.rb +55 -0
- data/lib/scimitar/rbac/schema/role_assignment.rb +20 -0
- data/lib/scimitar/rbac/version.rb +7 -0
- data/lib/scimitar/rbac.rb +71 -0
- metadata +109 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b929a00ba99bad2e6348687d9d35a9731efcb4518a0288211b256fa64e7c1617
|
|
4
|
+
data.tar.gz: c519a80e4dd3b773cb514c66d6ec3e2f155e7c0db1fa7287b6a7d011d14be1c4
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 04ea059854ada6d9b225514ba27ce4d1f52e808b0141cb3e381655a509cd1d173192fa51b13f7c52af65b9182b26ed39c0059eb00f51ecddf14f1787ac5258f0
|
|
7
|
+
data.tar.gz: a512523bcf0fbee0e2ab07d9cbbd75b211fa5d651c4d0f584e8b335eb973b30851ce044fe795a4fb95841e0d4940ac92a68df7214ba99aece08af0cef935e704
|
data/README.md
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# scimitar-rbac
|
|
2
|
+
|
|
3
|
+
An RBAC (Role-Based Access Control) profile for SCIM v2, built as an extension to the [scimitar](https://github.com/RIPAGlobal/scimitar) gem.
|
|
4
|
+
|
|
5
|
+
Based on the research paper:
|
|
6
|
+
|
|
7
|
+
> **Baumer, T., Muller, M., & Pernul, G. (2023).** *System for Cross-Domain Identity Management (SCIM): Survey and Enhancement With RBAC.* IEEE Access, 11, 86872-86894. DOI: [10.1109/ACCESS.2023.3304270](https://doi.org/10.1109/ACCESS.2023.3304270)
|
|
8
|
+
|
|
9
|
+
## What This Gem Does
|
|
10
|
+
|
|
11
|
+
The SCIM RFC family (RFC 7642-7644) focuses on identity data (Users, Groups) but only loosely prepares for RBAC. The `roles` and `entitlements` attributes on the User resource are specified in a "freestyle" notation without independent endpoints or the critical Role-to-Entitlement relationship. This leads to vendor-specific implementations that break interoperability.
|
|
12
|
+
|
|
13
|
+
This gem solves this by adding three first-class SCIM resource types:
|
|
14
|
+
|
|
15
|
+
| Resource | Endpoint | URN Schema ID |
|
|
16
|
+
|----------|----------|---------------|
|
|
17
|
+
| **Role** | `/Roles` | `urn:ietf:params:scim:schemas:extension:rbac:2.0:Role` |
|
|
18
|
+
| **Entitlement** | `/Entitlements` | `urn:ietf:params:scim:schemas:extension:rbac:2.0:Entitlement` |
|
|
19
|
+
| **Application** | `/Applications` | `urn:ietf:params:scim:schemas:extension:rbac:2.0:Application` |
|
|
20
|
+
|
|
21
|
+
### RBAC Data Model
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
0,n 0,n 0,n
|
|
25
|
+
User ────── Role ────── Entitlement ────── Application
|
|
26
|
+
│ │
|
|
27
|
+
0,n │ RH 0,n │ EH
|
|
28
|
+
│ │
|
|
29
|
+
Role Entitlement
|
|
30
|
+
(hierarchy) (hierarchy)
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **Role** — intermediate entity between Users and Entitlements (permissions)
|
|
34
|
+
- **Entitlement** — application-specific permission, belongs to one Application
|
|
35
|
+
- **Application** — target system / Service Provider
|
|
36
|
+
|
|
37
|
+
Key relationship: **Role ↔ Entitlement** (PA ⊆ P x R) — the assignment that standard SCIM lacks.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Add to your Gemfile:
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
gem "scimitar", "~> 2.0"
|
|
45
|
+
gem "scimitar-rbac", "~> 0.1"
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Then run:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
bundle install
|
|
52
|
+
rails generate scimitar_rbac:install
|
|
53
|
+
rails db:migrate
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Configuration
|
|
57
|
+
|
|
58
|
+
### Routes
|
|
59
|
+
|
|
60
|
+
Add RBAC resource routes to your `config/routes.rb`:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
namespace :scim_v2, path: "scim/v2" do
|
|
64
|
+
mount Scimitar::Engine, at: "/"
|
|
65
|
+
|
|
66
|
+
# Standard SCIM resources
|
|
67
|
+
get "Users", to: "scim_v2/users#index"
|
|
68
|
+
get "Users/:id", to: "scim_v2/users#show"
|
|
69
|
+
post "Users", to: "scim_v2/users#create"
|
|
70
|
+
# ...etc
|
|
71
|
+
|
|
72
|
+
# RBAC resources (all at once)
|
|
73
|
+
Scimitar::Rbac::RouteHelper.mount_rbac_routes(self,
|
|
74
|
+
roles_controller: "scim_v2/roles",
|
|
75
|
+
entitlements_controller: "scim_v2/entitlements",
|
|
76
|
+
applications_controller: "scim_v2/applications"
|
|
77
|
+
)
|
|
78
|
+
end
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Models
|
|
82
|
+
|
|
83
|
+
The generator creates `RbacRole`, `RbacEntitlement`, and `RbacApplication` models with the `Scimitar::Resources::Mixin` already configured. Customize the attribute maps to match your domain.
|
|
84
|
+
|
|
85
|
+
### Controllers
|
|
86
|
+
|
|
87
|
+
The generator creates controllers inheriting from `Scimitar::ActiveRecordBackedResourcesController`. Override `storage_scope` to add custom filtering:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
class ScimV2::RolesController < Scimitar::ActiveRecordBackedResourcesController
|
|
91
|
+
def storage_class
|
|
92
|
+
RbacRole
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def storage_scope
|
|
96
|
+
RbacRole.where(active: true)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## SCIM API Examples
|
|
102
|
+
|
|
103
|
+
### Create a Role
|
|
104
|
+
|
|
105
|
+
```http
|
|
106
|
+
POST /scim/v2/Roles
|
|
107
|
+
Content-Type: application/scim+json
|
|
108
|
+
|
|
109
|
+
{
|
|
110
|
+
"schemas": ["urn:ietf:params:scim:schemas:extension:rbac:2.0:Role"],
|
|
111
|
+
"displayName": "Billing Administrator",
|
|
112
|
+
"type": "business",
|
|
113
|
+
"description": "Full access to billing operations",
|
|
114
|
+
"entitlements": [
|
|
115
|
+
{ "value": "ent-uuid-1" },
|
|
116
|
+
{ "value": "ent-uuid-2" }
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### Create an Entitlement
|
|
122
|
+
|
|
123
|
+
```http
|
|
124
|
+
POST /scim/v2/Entitlements
|
|
125
|
+
Content-Type: application/scim+json
|
|
126
|
+
|
|
127
|
+
{
|
|
128
|
+
"schemas": ["urn:ietf:params:scim:schemas:extension:rbac:2.0:Entitlement"],
|
|
129
|
+
"displayName": "billing:write",
|
|
130
|
+
"type": "api_scope",
|
|
131
|
+
"application": { "value": "app-uuid-1" }
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Discover RBAC Resources
|
|
136
|
+
|
|
137
|
+
```http
|
|
138
|
+
GET /scim/v2/ResourceTypes
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Returns Role, Entitlement, and Application alongside standard User and Group resource types.
|
|
142
|
+
|
|
143
|
+
## Design Principles
|
|
144
|
+
|
|
145
|
+
Following the paper's guidance, this gem balances three design principles:
|
|
146
|
+
|
|
147
|
+
1. **Validity** — implements the NIST RBAC standard (Ferraiolo et al., 2001) with proper User-Role-Entitlement relationships, hierarchies, and cardinality constraints
|
|
148
|
+
2. **Simplicity** — minimal overhead, reuses SCIM conventions, no unnecessary resources
|
|
149
|
+
3. **Flexibility** — extensible schemas, custom attributes via SCIM extension mechanism
|
|
150
|
+
|
|
151
|
+
## Future Extensions
|
|
152
|
+
|
|
153
|
+
The following resources from the full RBAC profile can be added in future versions:
|
|
154
|
+
|
|
155
|
+
- **Account** — user's identity within a specific application
|
|
156
|
+
- **SoD** (Separation of Duty) — constraints on mutually exclusive roles/entitlements
|
|
157
|
+
- **Session** — runtime activation of roles and entitlements
|
|
158
|
+
|
|
159
|
+
## References
|
|
160
|
+
|
|
161
|
+
- [RFC 7642](https://www.rfc-editor.org/info/rfc7642) — SCIM: Definitions, Overview, Concepts, and Requirements
|
|
162
|
+
- [RFC 7643](https://www.rfc-editor.org/info/rfc7643) — SCIM: Core Schema
|
|
163
|
+
- [RFC 7644](https://www.rfc-editor.org/info/rfc7644) — SCIM: Protocol
|
|
164
|
+
- [Zollner (2022)](https://datatracker.ietf.org/doc/draft-ietf-scim-roles-entitlements/) — SCIM Roles and Entitlements Extension
|
|
165
|
+
- [NIST RBAC](https://doi.org/10.1145/501978.501980) — Proposed NIST Standard for Role-Based Access Control
|
|
166
|
+
- [RBAC4SCIM Swagger](https://rbac4scim.github.io/api) — Reference API prototype from the paper
|
|
167
|
+
|
|
168
|
+
## License
|
|
169
|
+
|
|
170
|
+
MIT
|
data/Rakefile
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators"
|
|
4
|
+
require "rails/generators/active_record"
|
|
5
|
+
|
|
6
|
+
module ScimitarRbac
|
|
7
|
+
# Generates migrations, models, and controllers for RBAC SCIM resources.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# rails generate scimitar_rbac:install
|
|
11
|
+
#
|
|
12
|
+
class InstallGenerator < Rails::Generators::Base
|
|
13
|
+
include ActiveRecord::Generators::Migration
|
|
14
|
+
|
|
15
|
+
source_root File.expand_path("templates", __dir__)
|
|
16
|
+
|
|
17
|
+
desc "Creates migrations, models, and controllers for SCIM RBAC resources (Role, Entitlement, Application)."
|
|
18
|
+
|
|
19
|
+
def create_migration_file
|
|
20
|
+
migration_template "migration.rb.erb", "db/migrate/create_scimitar_rbac_tables.rb"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def create_model_files
|
|
24
|
+
template "role_model.rb.erb", "app/models/rbac_role.rb"
|
|
25
|
+
template "entitlement_model.rb.erb", "app/models/rbac_entitlement.rb"
|
|
26
|
+
template "application_model.rb.erb", "app/models/rbac_application.rb"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_controller_files
|
|
30
|
+
template "roles_controller.rb.erb", "app/controllers/scim_v2/roles_controller.rb"
|
|
31
|
+
template "entitlements_controller.rb.erb", "app/controllers/scim_v2/entitlements_controller.rb"
|
|
32
|
+
template "applications_controller.rb.erb", "app/controllers/scim_v2/applications_controller.rb"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def display_post_install_message
|
|
36
|
+
say ""
|
|
37
|
+
say "SCIM RBAC resources have been generated!", :green
|
|
38
|
+
say ""
|
|
39
|
+
say "Next steps:"
|
|
40
|
+
say " 1. Run migrations: rails db:migrate"
|
|
41
|
+
say " 2. Add routes to config/routes.rb:"
|
|
42
|
+
say ""
|
|
43
|
+
say " namespace :scim_v2, path: 'scim/v2' do"
|
|
44
|
+
say " mount Scimitar::Engine, at: '/'"
|
|
45
|
+
say " Scimitar::Rbac::RouteHelper.mount_rbac_routes(self,"
|
|
46
|
+
say " roles_controller: 'scim_v2/roles',"
|
|
47
|
+
say " entitlements_controller: 'scim_v2/entitlements',"
|
|
48
|
+
say " applications_controller: 'scim_v2/applications'"
|
|
49
|
+
say " )"
|
|
50
|
+
say " end"
|
|
51
|
+
say ""
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def migration_version
|
|
57
|
+
"[#{ActiveRecord::Migration.current_version}]"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RbacApplication < ApplicationRecord
|
|
4
|
+
self.table_name = "rbac_applications"
|
|
5
|
+
|
|
6
|
+
has_many :entitlements,
|
|
7
|
+
class_name: "RbacEntitlement",
|
|
8
|
+
foreign_key: :application_id,
|
|
9
|
+
dependent: :restrict_with_error
|
|
10
|
+
|
|
11
|
+
validates :display_name, presence: true
|
|
12
|
+
|
|
13
|
+
# --- SCIM Configuration ---
|
|
14
|
+
|
|
15
|
+
def self.scim_resource_type
|
|
16
|
+
Scimitar::Resources::Rbac::Application
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.scim_attributes_map
|
|
20
|
+
{
|
|
21
|
+
id: :id,
|
|
22
|
+
externalId: :external_id,
|
|
23
|
+
displayName: :display_name,
|
|
24
|
+
description: :description,
|
|
25
|
+
active: :active,
|
|
26
|
+
entitlements: [
|
|
27
|
+
{
|
|
28
|
+
list: :entitlements,
|
|
29
|
+
using: { value: :id, display: :display_name }
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.scim_mutable_attributes
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def self.scim_queryable_attributes
|
|
40
|
+
{
|
|
41
|
+
"displayName" => { column: :display_name },
|
|
42
|
+
"active" => { column: :active },
|
|
43
|
+
"externalId" => { column: :external_id }
|
|
44
|
+
}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def self.scim_timestamps_map
|
|
48
|
+
{
|
|
49
|
+
created: :created_at,
|
|
50
|
+
lastModified: :updated_at
|
|
51
|
+
}
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
include Scimitar::Resources::Mixin
|
|
55
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScimV2
|
|
4
|
+
class ApplicationsController < Scimitar::ActiveRecordBackedResourcesController
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def storage_class
|
|
8
|
+
RbacApplication
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def storage_scope
|
|
12
|
+
RbacApplication.all
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RbacEntitlement < ApplicationRecord
|
|
4
|
+
self.table_name = "rbac_entitlements"
|
|
5
|
+
|
|
6
|
+
# Application reference
|
|
7
|
+
belongs_to :application,
|
|
8
|
+
class_name: "RbacApplication",
|
|
9
|
+
foreign_key: :application_id,
|
|
10
|
+
optional: true
|
|
11
|
+
|
|
12
|
+
# Role assignments (PA ⊆ P × R, viewed from the entitlement side)
|
|
13
|
+
has_many :role_entitlements,
|
|
14
|
+
class_name: "RbacRoleEntitlement",
|
|
15
|
+
foreign_key: :entitlement_id,
|
|
16
|
+
dependent: :destroy,
|
|
17
|
+
inverse_of: :entitlement
|
|
18
|
+
|
|
19
|
+
has_many :roles,
|
|
20
|
+
through: :role_entitlements,
|
|
21
|
+
class_name: "RbacRole"
|
|
22
|
+
|
|
23
|
+
# Entitlement hierarchy — parent relationships
|
|
24
|
+
has_many :parent_links,
|
|
25
|
+
class_name: "RbacEntitlementHierarchy",
|
|
26
|
+
foreign_key: :child_entitlement_id,
|
|
27
|
+
dependent: :destroy,
|
|
28
|
+
inverse_of: :child_entitlement
|
|
29
|
+
|
|
30
|
+
has_many :parent_entitlements,
|
|
31
|
+
through: :parent_links,
|
|
32
|
+
source: :parent_entitlement,
|
|
33
|
+
class_name: "RbacEntitlement"
|
|
34
|
+
|
|
35
|
+
# Entitlement hierarchy — child relationships
|
|
36
|
+
has_many :child_links,
|
|
37
|
+
class_name: "RbacEntitlementHierarchy",
|
|
38
|
+
foreign_key: :parent_entitlement_id,
|
|
39
|
+
dependent: :destroy,
|
|
40
|
+
inverse_of: :parent_entitlement
|
|
41
|
+
|
|
42
|
+
has_many :child_entitlements,
|
|
43
|
+
through: :child_links,
|
|
44
|
+
source: :child_entitlement,
|
|
45
|
+
class_name: "RbacEntitlement"
|
|
46
|
+
|
|
47
|
+
validates :display_name, presence: true
|
|
48
|
+
|
|
49
|
+
# --- SCIM Configuration ---
|
|
50
|
+
|
|
51
|
+
def self.scim_resource_type
|
|
52
|
+
Scimitar::Resources::Rbac::Entitlement
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.scim_attributes_map
|
|
56
|
+
{
|
|
57
|
+
id: :id,
|
|
58
|
+
externalId: :external_id,
|
|
59
|
+
displayName: :display_name,
|
|
60
|
+
type: :entitlement_type,
|
|
61
|
+
description: :description,
|
|
62
|
+
limitedAssignmentsPermitted: :limited_assignments_permitted,
|
|
63
|
+
totalAssignmentsPermitted: :total_assignments_permitted,
|
|
64
|
+
application: {
|
|
65
|
+
value: :application_id,
|
|
66
|
+
display: :application_name
|
|
67
|
+
},
|
|
68
|
+
roles: [
|
|
69
|
+
{
|
|
70
|
+
list: :roles,
|
|
71
|
+
using: { value: :id, display: :display_name }
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
parentEntitlements: [
|
|
75
|
+
{
|
|
76
|
+
list: :parent_entitlements,
|
|
77
|
+
using: { value: :id, display: :display_name }
|
|
78
|
+
}
|
|
79
|
+
],
|
|
80
|
+
childEntitlements: [
|
|
81
|
+
{
|
|
82
|
+
list: :child_entitlements,
|
|
83
|
+
using: { value: :id, display: :display_name }
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def self.scim_mutable_attributes
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.scim_queryable_attributes
|
|
94
|
+
{
|
|
95
|
+
"displayName" => { column: :display_name },
|
|
96
|
+
"type" => { column: :entitlement_type },
|
|
97
|
+
"externalId" => { column: :external_id }
|
|
98
|
+
}
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def self.scim_timestamps_map
|
|
102
|
+
{
|
|
103
|
+
created: :created_at,
|
|
104
|
+
lastModified: :updated_at
|
|
105
|
+
}
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
include Scimitar::Resources::Mixin
|
|
109
|
+
|
|
110
|
+
# Helper for SCIM attribute mapping
|
|
111
|
+
def application_name
|
|
112
|
+
application&.display_name
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ScimV2
|
|
4
|
+
class EntitlementsController < Scimitar::ActiveRecordBackedResourcesController
|
|
5
|
+
protected
|
|
6
|
+
|
|
7
|
+
def storage_class
|
|
8
|
+
RbacEntitlement
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def storage_scope
|
|
12
|
+
RbacEntitlement.all
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateScimitarRbacTables < ActiveRecord::Migration<%= migration_version %>
|
|
4
|
+
def change
|
|
5
|
+
# Enable UUID support if using PostgreSQL
|
|
6
|
+
enable_extension "pgcrypto" unless extension_enabled?("pgcrypto")
|
|
7
|
+
|
|
8
|
+
create_table :rbac_applications, id: :uuid do |t|
|
|
9
|
+
t.string :external_id
|
|
10
|
+
t.string :display_name, null: false
|
|
11
|
+
t.text :description
|
|
12
|
+
t.boolean :active, default: true, null: false
|
|
13
|
+
|
|
14
|
+
t.timestamps
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
create_table :rbac_roles, id: :uuid do |t|
|
|
18
|
+
t.string :external_id
|
|
19
|
+
t.string :display_name, null: false
|
|
20
|
+
t.string :role_type
|
|
21
|
+
t.text :description
|
|
22
|
+
t.integer :limited_assignments_permitted
|
|
23
|
+
t.integer :total_assignments_permitted
|
|
24
|
+
|
|
25
|
+
t.timestamps
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
create_table :rbac_entitlements, id: :uuid do |t|
|
|
29
|
+
t.string :external_id
|
|
30
|
+
t.string :display_name, null: false
|
|
31
|
+
t.string :entitlement_type
|
|
32
|
+
t.text :description
|
|
33
|
+
t.uuid :application_id
|
|
34
|
+
t.integer :limited_assignments_permitted
|
|
35
|
+
t.integer :total_assignments_permitted
|
|
36
|
+
|
|
37
|
+
t.timestamps
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Role ↔ Entitlement assignments (PA ⊆ P × R)
|
|
41
|
+
create_table :rbac_role_entitlements, id: :uuid do |t|
|
|
42
|
+
t.uuid :role_id, null: false
|
|
43
|
+
t.uuid :entitlement_id, null: false
|
|
44
|
+
|
|
45
|
+
t.timestamps
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Role hierarchy (RH ⊆ R × R)
|
|
49
|
+
create_table :rbac_role_hierarchies, id: :uuid do |t|
|
|
50
|
+
t.uuid :parent_role_id, null: false
|
|
51
|
+
t.uuid :child_role_id, null: false
|
|
52
|
+
|
|
53
|
+
t.timestamps
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Entitlement hierarchy
|
|
57
|
+
create_table :rbac_entitlement_hierarchies, id: :uuid do |t|
|
|
58
|
+
t.uuid :parent_entitlement_id, null: false
|
|
59
|
+
t.uuid :child_entitlement_id, null: false
|
|
60
|
+
|
|
61
|
+
t.timestamps
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
add_index :rbac_applications, :display_name
|
|
65
|
+
add_index :rbac_roles, :display_name
|
|
66
|
+
add_index :rbac_entitlements, :display_name
|
|
67
|
+
add_index :rbac_entitlements, :application_id
|
|
68
|
+
|
|
69
|
+
add_index :rbac_role_entitlements, [:role_id, :entitlement_id], unique: true
|
|
70
|
+
add_index :rbac_role_entitlements, :entitlement_id
|
|
71
|
+
|
|
72
|
+
add_index :rbac_role_hierarchies, [:parent_role_id, :child_role_id], unique: true
|
|
73
|
+
add_index :rbac_role_hierarchies, :child_role_id
|
|
74
|
+
|
|
75
|
+
add_index :rbac_entitlement_hierarchies, [:parent_entitlement_id, :child_entitlement_id],
|
|
76
|
+
unique: true, name: "idx_rbac_ent_hier_parent_child"
|
|
77
|
+
add_index :rbac_entitlement_hierarchies, :child_entitlement_id,
|
|
78
|
+
name: "idx_rbac_ent_hier_child"
|
|
79
|
+
|
|
80
|
+
add_foreign_key :rbac_entitlements, :rbac_applications, column: :application_id
|
|
81
|
+
|
|
82
|
+
add_foreign_key :rbac_role_entitlements, :rbac_roles, column: :role_id
|
|
83
|
+
add_foreign_key :rbac_role_entitlements, :rbac_entitlements, column: :entitlement_id
|
|
84
|
+
|
|
85
|
+
add_foreign_key :rbac_role_hierarchies, :rbac_roles, column: :parent_role_id
|
|
86
|
+
add_foreign_key :rbac_role_hierarchies, :rbac_roles, column: :child_role_id
|
|
87
|
+
|
|
88
|
+
add_foreign_key :rbac_entitlement_hierarchies, :rbac_entitlements, column: :parent_entitlement_id
|
|
89
|
+
add_foreign_key :rbac_entitlement_hierarchies, :rbac_entitlements, column: :child_entitlement_id
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RbacRole < ApplicationRecord
|
|
4
|
+
self.table_name = "rbac_roles"
|
|
5
|
+
|
|
6
|
+
# Entitlement assignments (PA ⊆ P × R)
|
|
7
|
+
has_many :role_entitlements,
|
|
8
|
+
class_name: "RbacRoleEntitlement",
|
|
9
|
+
foreign_key: :role_id,
|
|
10
|
+
dependent: :destroy,
|
|
11
|
+
inverse_of: :role
|
|
12
|
+
|
|
13
|
+
has_many :entitlements,
|
|
14
|
+
through: :role_entitlements,
|
|
15
|
+
class_name: "RbacEntitlement"
|
|
16
|
+
|
|
17
|
+
# Role hierarchy — parent relationships
|
|
18
|
+
has_many :parent_links,
|
|
19
|
+
class_name: "RbacRoleHierarchy",
|
|
20
|
+
foreign_key: :child_role_id,
|
|
21
|
+
dependent: :destroy,
|
|
22
|
+
inverse_of: :child_role
|
|
23
|
+
|
|
24
|
+
has_many :parent_roles,
|
|
25
|
+
through: :parent_links,
|
|
26
|
+
source: :parent_role,
|
|
27
|
+
class_name: "RbacRole"
|
|
28
|
+
|
|
29
|
+
# Role hierarchy — child relationships
|
|
30
|
+
has_many :child_links,
|
|
31
|
+
class_name: "RbacRoleHierarchy",
|
|
32
|
+
foreign_key: :parent_role_id,
|
|
33
|
+
dependent: :destroy,
|
|
34
|
+
inverse_of: :parent_role
|
|
35
|
+
|
|
36
|
+
has_many :child_roles,
|
|
37
|
+
through: :child_links,
|
|
38
|
+
source: :child_role,
|
|
39
|
+
class_name: "RbacRole"
|
|
40
|
+
|
|
41
|
+
validates :display_name, presence: true
|
|
42
|
+
|
|
43
|
+
# --- SCIM Configuration ---
|
|
44
|
+
|
|
45
|
+
def self.scim_resource_type
|
|
46
|
+
Scimitar::Resources::Rbac::Role
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.scim_attributes_map
|
|
50
|
+
{
|
|
51
|
+
id: :id,
|
|
52
|
+
externalId: :external_id,
|
|
53
|
+
displayName: :display_name,
|
|
54
|
+
type: :role_type,
|
|
55
|
+
description: :description,
|
|
56
|
+
limitedAssignmentsPermitted: :limited_assignments_permitted,
|
|
57
|
+
totalAssignmentsPermitted: :total_assignments_permitted,
|
|
58
|
+
entitlements: [
|
|
59
|
+
{
|
|
60
|
+
list: :entitlements,
|
|
61
|
+
using: { value: :id, display: :display_name }
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
parentRoles: [
|
|
65
|
+
{
|
|
66
|
+
list: :parent_roles,
|
|
67
|
+
using: { value: :id, display: :display_name }
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
childRoles: [
|
|
71
|
+
{
|
|
72
|
+
list: :child_roles,
|
|
73
|
+
using: { value: :id, display: :display_name }
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.scim_mutable_attributes
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def self.scim_queryable_attributes
|
|
84
|
+
{
|
|
85
|
+
"displayName" => { column: :display_name },
|
|
86
|
+
"type" => { column: :role_type },
|
|
87
|
+
"externalId" => { column: :external_id }
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def self.scim_timestamps_map
|
|
92
|
+
{
|
|
93
|
+
created: :created_at,
|
|
94
|
+
lastModified: :updated_at
|
|
95
|
+
}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
include Scimitar::Resources::Mixin
|
|
99
|
+
|
|
100
|
+
# Computed attribute
|
|
101
|
+
def total_assignments_used
|
|
102
|
+
# Override this with actual count logic in your application
|
|
103
|
+
0
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module ComplexTypes
|
|
5
|
+
module Rbac
|
|
6
|
+
# Represents a reference to an Application resource.
|
|
7
|
+
# Used in Entitlement and Account resources to link back
|
|
8
|
+
# to the target application/service provider.
|
|
9
|
+
class ApplicationReference < Scimitar::ComplexTypes::Base
|
|
10
|
+
set_schema Scimitar::Schema::Rbac::ApplicationReference
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module ComplexTypes
|
|
5
|
+
module Rbac
|
|
6
|
+
# Represents a structured entitlement (permission) assignment.
|
|
7
|
+
# Replaces the "freestyle" entitlement notation in SCIM core (RFC 7643)
|
|
8
|
+
# with a proper reference to an Entitlement resource.
|
|
9
|
+
class EntitlementAssignment < Scimitar::ComplexTypes::Base
|
|
10
|
+
set_schema Scimitar::Schema::Rbac::EntitlementAssignment
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module ComplexTypes
|
|
5
|
+
module Rbac
|
|
6
|
+
# Represents a reference to a parent or child in a role/entitlement
|
|
7
|
+
# hierarchy. Used for parentRoles, childRoles, parentEntitlements,
|
|
8
|
+
# and childEntitlements multi-valued attributes.
|
|
9
|
+
#
|
|
10
|
+
# Based on the RBAC data model from Baumer et al. (2023), which defines
|
|
11
|
+
# many-to-many hierarchies RH for roles and entitlements.
|
|
12
|
+
class HierarchyMember < Scimitar::ComplexTypes::Base
|
|
13
|
+
set_schema Scimitar::Schema::Rbac::HierarchyMember
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module ComplexTypes
|
|
5
|
+
module Rbac
|
|
6
|
+
# Represents a structured role assignment for a User or Account.
|
|
7
|
+
# Replaces the "freestyle" role notation in SCIM core (RFC 7643)
|
|
8
|
+
# with a proper reference to a Role resource.
|
|
9
|
+
class RoleAssignment < Scimitar::ComplexTypes::Base
|
|
10
|
+
set_schema Scimitar::Schema::Rbac::RoleAssignment
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Rbac
|
|
5
|
+
class Engine < ::Rails::Engine
|
|
6
|
+
initializer "scimitar_rbac.register_resources" do
|
|
7
|
+
Rails.application.config.to_prepare do
|
|
8
|
+
Scimitar::Rbac.register_resources!
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Resources
|
|
5
|
+
module Rbac
|
|
6
|
+
# SCIM Resource class for the RBAC Application (target system / SP).
|
|
7
|
+
class Application < Scimitar::Resources::Base
|
|
8
|
+
set_schema Scimitar::Schema::Rbac::Application
|
|
9
|
+
|
|
10
|
+
def self.endpoint
|
|
11
|
+
"/Applications"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Resources
|
|
5
|
+
module Rbac
|
|
6
|
+
# SCIM Resource class for the RBAC Entitlement (permission).
|
|
7
|
+
class Entitlement < Scimitar::Resources::Base
|
|
8
|
+
set_schema Scimitar::Schema::Rbac::Entitlement
|
|
9
|
+
|
|
10
|
+
def self.endpoint
|
|
11
|
+
"/Entitlements"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Resources
|
|
5
|
+
module Rbac
|
|
6
|
+
# SCIM Resource class for the RBAC Role.
|
|
7
|
+
class Role < Scimitar::Resources::Base
|
|
8
|
+
set_schema Scimitar::Schema::Rbac::Role
|
|
9
|
+
|
|
10
|
+
def self.endpoint
|
|
11
|
+
"/Roles"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Rbac
|
|
5
|
+
# Provides a convenience method for mounting RBAC SCIM resource routes
|
|
6
|
+
# in a Rails application's routes.rb.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
#
|
|
10
|
+
# # config/routes.rb
|
|
11
|
+
# Rails.application.routes.draw do
|
|
12
|
+
# namespace :scim_v2, path: "scim/v2" do
|
|
13
|
+
# mount Scimitar::Engine, at: "/"
|
|
14
|
+
#
|
|
15
|
+
# # Mount standard SCIM resources (Users, Groups) manually...
|
|
16
|
+
#
|
|
17
|
+
# # Mount all RBAC resources at once:
|
|
18
|
+
# Scimitar::Rbac::RouteHelper.mount_rbac_routes(self,
|
|
19
|
+
# roles_controller: "scim_v2/roles",
|
|
20
|
+
# entitlements_controller: "scim_v2/entitlements",
|
|
21
|
+
# applications_controller: "scim_v2/applications"
|
|
22
|
+
# )
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
module RouteHelper
|
|
27
|
+
# Mount CRUD routes for all RBAC resources.
|
|
28
|
+
#
|
|
29
|
+
# @param router [ActionDispatch::Routing::RouteSet] The router (pass `self` from routes.rb)
|
|
30
|
+
# @param options [Hash] Controller names for each resource
|
|
31
|
+
# @option options [String] :roles_controller Controller for Roles (e.g., "scim_v2/roles")
|
|
32
|
+
# @option options [String] :entitlements_controller Controller for Entitlements
|
|
33
|
+
# @option options [String] :applications_controller Controller for Applications
|
|
34
|
+
def self.mount_rbac_routes(router, **options)
|
|
35
|
+
if options[:roles_controller]
|
|
36
|
+
mount_resource_routes(router, "Roles", options[:roles_controller])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
if options[:entitlements_controller]
|
|
40
|
+
mount_resource_routes(router, "Entitlements", options[:entitlements_controller])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
if options[:applications_controller]
|
|
44
|
+
mount_resource_routes(router, "Applications", options[:applications_controller])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Mount standard SCIM CRUD routes for a single resource.
|
|
49
|
+
#
|
|
50
|
+
# @param router [ActionDispatch::Routing::RouteSet] The router
|
|
51
|
+
# @param resource_name [String] The SCIM resource path (e.g., "Roles")
|
|
52
|
+
# @param controller [String] The controller name (e.g., "scim_v2/roles")
|
|
53
|
+
def self.mount_resource_routes(router, resource_name, controller)
|
|
54
|
+
router.instance_exec do
|
|
55
|
+
get resource_name, to: "#{controller}#index"
|
|
56
|
+
get "#{resource_name}/:id", to: "#{controller}#show"
|
|
57
|
+
post resource_name, to: "#{controller}#create"
|
|
58
|
+
put "#{resource_name}/:id", to: "#{controller}#replace"
|
|
59
|
+
patch "#{resource_name}/:id", to: "#{controller}#update"
|
|
60
|
+
delete "#{resource_name}/:id", to: "#{controller}#destroy"
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Schema
|
|
5
|
+
module Rbac
|
|
6
|
+
# SCIM schema for the Application resource.
|
|
7
|
+
# Applications represent target systems / Service Providers (SPs).
|
|
8
|
+
# Entitlements are application-specific (each belongs to one application).
|
|
9
|
+
class Application < Scimitar::Schema::Base
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
super(
|
|
12
|
+
name: "Application",
|
|
13
|
+
id: self.class.id,
|
|
14
|
+
description: "Represents a target application (service provider) in the RBAC model.",
|
|
15
|
+
scim_attributes: self.class.scim_attributes
|
|
16
|
+
)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.id
|
|
20
|
+
"urn:ietf:params:scim:schemas:extension:rbac:2.0:Application"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.scim_attributes
|
|
24
|
+
@scim_attributes ||= [
|
|
25
|
+
Scimitar::Schema::Attribute.new(name: "displayName", type: "string", required: true),
|
|
26
|
+
Scimitar::Schema::Attribute.new(name: "description", type: "string"),
|
|
27
|
+
Scimitar::Schema::Attribute.new(name: "active", type: "boolean"),
|
|
28
|
+
|
|
29
|
+
Scimitar::Schema::Attribute.new(name: "entitlements", multiValued: true,
|
|
30
|
+
complexType: Scimitar::ComplexTypes::Rbac::EntitlementAssignment, mutability: "readOnly"),
|
|
31
|
+
]
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Schema
|
|
5
|
+
module Rbac
|
|
6
|
+
# Schema for the ApplicationReference complex type.
|
|
7
|
+
# Defines sub-attributes for references to Application resources.
|
|
8
|
+
class ApplicationReference < Scimitar::Schema::Base
|
|
9
|
+
def self.scim_attributes
|
|
10
|
+
@scim_attributes ||= [
|
|
11
|
+
Scimitar::Schema::Attribute.new(name: "value", type: "string", required: true),
|
|
12
|
+
Scimitar::Schema::Attribute.new(name: "display", type: "string", mutability: "readOnly"),
|
|
13
|
+
]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Schema
|
|
5
|
+
module Rbac
|
|
6
|
+
# SCIM schema for the Entitlement (permission) resource.
|
|
7
|
+
# Entitlements represent application-specific permissions, each belonging
|
|
8
|
+
# to one Application. The Role<->Entitlement relationship is the key
|
|
9
|
+
# missing link in standard SCIM that this profile addresses.
|
|
10
|
+
class Entitlement < Scimitar::Schema::Base
|
|
11
|
+
def initialize(options = {})
|
|
12
|
+
super(
|
|
13
|
+
name: "Entitlement",
|
|
14
|
+
id: self.class.id,
|
|
15
|
+
description: "Represents an RBAC Entitlement (permission) — an application-specific access right assignable to Roles.",
|
|
16
|
+
scim_attributes: self.class.scim_attributes
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.id
|
|
21
|
+
"urn:ietf:params:scim:schemas:extension:rbac:2.0:Entitlement"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.scim_attributes
|
|
25
|
+
@scim_attributes ||= [
|
|
26
|
+
Scimitar::Schema::Attribute.new(name: "displayName", type: "string", required: true),
|
|
27
|
+
Scimitar::Schema::Attribute.new(name: "type", type: "string"),
|
|
28
|
+
Scimitar::Schema::Attribute.new(name: "description", type: "string"),
|
|
29
|
+
|
|
30
|
+
Scimitar::Schema::Attribute.new(name: "application",
|
|
31
|
+
complexType: Scimitar::ComplexTypes::Rbac::ApplicationReference),
|
|
32
|
+
|
|
33
|
+
Scimitar::Schema::Attribute.new(name: "roles", multiValued: true,
|
|
34
|
+
complexType: Scimitar::ComplexTypes::Rbac::RoleAssignment, mutability: "readOnly"),
|
|
35
|
+
|
|
36
|
+
Scimitar::Schema::Attribute.new(name: "parentEntitlements", multiValued: true,
|
|
37
|
+
complexType: Scimitar::ComplexTypes::Rbac::HierarchyMember),
|
|
38
|
+
|
|
39
|
+
Scimitar::Schema::Attribute.new(name: "childEntitlements", multiValued: true,
|
|
40
|
+
complexType: Scimitar::ComplexTypes::Rbac::HierarchyMember, mutability: "readOnly"),
|
|
41
|
+
|
|
42
|
+
Scimitar::Schema::Attribute.new(name: "limitedAssignmentsPermitted", type: "integer"),
|
|
43
|
+
Scimitar::Schema::Attribute.new(name: "totalAssignmentsPermitted", type: "integer"),
|
|
44
|
+
Scimitar::Schema::Attribute.new(name: "totalAssignmentsUsed", type: "integer", mutability: "readOnly"),
|
|
45
|
+
]
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Schema
|
|
5
|
+
module Rbac
|
|
6
|
+
# Schema for the EntitlementAssignment complex type.
|
|
7
|
+
# Replaces the "freestyle" entitlement notation in SCIM core (RFC 7643)
|
|
8
|
+
# with structured sub-attributes referencing an Entitlement resource.
|
|
9
|
+
class EntitlementAssignment < Scimitar::Schema::Base
|
|
10
|
+
def self.scim_attributes
|
|
11
|
+
@scim_attributes ||= [
|
|
12
|
+
Scimitar::Schema::Attribute.new(name: "value", type: "string", required: true),
|
|
13
|
+
Scimitar::Schema::Attribute.new(name: "display", type: "string", mutability: "readOnly"),
|
|
14
|
+
Scimitar::Schema::Attribute.new(name: "type", type: "string"),
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Schema
|
|
5
|
+
module Rbac
|
|
6
|
+
# Schema for the HierarchyMember complex type.
|
|
7
|
+
# Defines the sub-attributes for role/entitlement hierarchy references.
|
|
8
|
+
class HierarchyMember < Scimitar::Schema::Base
|
|
9
|
+
def self.scim_attributes
|
|
10
|
+
@scim_attributes ||= [
|
|
11
|
+
Scimitar::Schema::Attribute.new(name: "value", type: "string", required: true),
|
|
12
|
+
Scimitar::Schema::Attribute.new(name: "display", type: "string", mutability: "readOnly"),
|
|
13
|
+
Scimitar::Schema::Attribute.new(name: "type", type: "string", mutability: "readOnly"),
|
|
14
|
+
]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Schema
|
|
5
|
+
module Rbac
|
|
6
|
+
# SCIM schema for the Role resource as defined in the RBAC profile
|
|
7
|
+
# for SCIM (Baumer et al., 2023).
|
|
8
|
+
#
|
|
9
|
+
# Roles are the central RBAC entity, serving as an intermediate between
|
|
10
|
+
# Users and Entitlements (permissions). The schema supports:
|
|
11
|
+
#
|
|
12
|
+
# - Core RBAC: User <-> Role <-> Entitlement assignments
|
|
13
|
+
# - Hierarchical RBAC: Role hierarchies via parentRoles/childRoles
|
|
14
|
+
# - Constrained RBAC: Cardinality constraints on assignments
|
|
15
|
+
class Role < Scimitar::Schema::Base
|
|
16
|
+
def initialize(options = {})
|
|
17
|
+
super(
|
|
18
|
+
name: "Role",
|
|
19
|
+
id: self.class.id,
|
|
20
|
+
description: "Represents an RBAC Role — an intermediate entity between Users and Entitlements (permissions).",
|
|
21
|
+
scim_attributes: self.class.scim_attributes
|
|
22
|
+
)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def self.id
|
|
26
|
+
"urn:ietf:params:scim:schemas:extension:rbac:2.0:Role"
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.scim_attributes
|
|
30
|
+
@scim_attributes ||= [
|
|
31
|
+
Scimitar::Schema::Attribute.new(name: "displayName", type: "string", required: true),
|
|
32
|
+
Scimitar::Schema::Attribute.new(name: "type", type: "string"),
|
|
33
|
+
Scimitar::Schema::Attribute.new(name: "description", type: "string"),
|
|
34
|
+
|
|
35
|
+
Scimitar::Schema::Attribute.new(name: "users", multiValued: true,
|
|
36
|
+
complexType: Scimitar::ComplexTypes::ReferenceMember, mutability: "readOnly"),
|
|
37
|
+
|
|
38
|
+
Scimitar::Schema::Attribute.new(name: "entitlements", multiValued: true,
|
|
39
|
+
complexType: Scimitar::ComplexTypes::Rbac::EntitlementAssignment),
|
|
40
|
+
|
|
41
|
+
Scimitar::Schema::Attribute.new(name: "parentRoles", multiValued: true,
|
|
42
|
+
complexType: Scimitar::ComplexTypes::Rbac::HierarchyMember),
|
|
43
|
+
|
|
44
|
+
Scimitar::Schema::Attribute.new(name: "childRoles", multiValued: true,
|
|
45
|
+
complexType: Scimitar::ComplexTypes::Rbac::HierarchyMember, mutability: "readOnly"),
|
|
46
|
+
|
|
47
|
+
Scimitar::Schema::Attribute.new(name: "limitedAssignmentsPermitted", type: "integer"),
|
|
48
|
+
Scimitar::Schema::Attribute.new(name: "totalAssignmentsPermitted", type: "integer"),
|
|
49
|
+
Scimitar::Schema::Attribute.new(name: "totalAssignmentsUsed", type: "integer", mutability: "readOnly"),
|
|
50
|
+
]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Scimitar
|
|
4
|
+
module Schema
|
|
5
|
+
module Rbac
|
|
6
|
+
# Schema for the RoleAssignment complex type.
|
|
7
|
+
# Replaces the "freestyle" role notation in SCIM core (RFC 7643)
|
|
8
|
+
# with structured sub-attributes referencing a Role resource.
|
|
9
|
+
class RoleAssignment < Scimitar::Schema::Base
|
|
10
|
+
def self.scim_attributes
|
|
11
|
+
@scim_attributes ||= [
|
|
12
|
+
Scimitar::Schema::Attribute.new(name: "value", type: "string", required: true),
|
|
13
|
+
Scimitar::Schema::Attribute.new(name: "display", type: "string", mutability: "readOnly"),
|
|
14
|
+
Scimitar::Schema::Attribute.new(name: "type", type: "string"),
|
|
15
|
+
]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "scimitar"
|
|
4
|
+
|
|
5
|
+
require_relative "rbac/version"
|
|
6
|
+
require_relative "rbac/route_helper"
|
|
7
|
+
|
|
8
|
+
module Scimitar
|
|
9
|
+
module Rbac
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
|
|
12
|
+
# URN prefix for RBAC extension schemas
|
|
13
|
+
URN_PREFIX = "urn:ietf:params:scim:schemas:extension:rbac:2.0"
|
|
14
|
+
|
|
15
|
+
# Eagerly load all RBAC components. Called after Rails and scimitar
|
|
16
|
+
# are fully initialized (via Engine initializer or manually).
|
|
17
|
+
def self.load!
|
|
18
|
+
return if @loaded
|
|
19
|
+
|
|
20
|
+
# Complex type schemas (must be loaded before the complex types
|
|
21
|
+
# that reference them and before resource schemas that use them)
|
|
22
|
+
require_relative "rbac/schema/hierarchy_member"
|
|
23
|
+
require_relative "rbac/schema/role_assignment"
|
|
24
|
+
require_relative "rbac/schema/entitlement_assignment"
|
|
25
|
+
require_relative "rbac/schema/application_reference"
|
|
26
|
+
|
|
27
|
+
# Complex types (must be loaded before resource schemas that reference them)
|
|
28
|
+
require_relative "rbac/complex_types/hierarchy_member"
|
|
29
|
+
require_relative "rbac/complex_types/role_assignment"
|
|
30
|
+
require_relative "rbac/complex_types/entitlement_assignment"
|
|
31
|
+
require_relative "rbac/complex_types/application_reference"
|
|
32
|
+
|
|
33
|
+
# Resource schemas
|
|
34
|
+
require_relative "rbac/schema/role"
|
|
35
|
+
require_relative "rbac/schema/entitlement"
|
|
36
|
+
require_relative "rbac/schema/application"
|
|
37
|
+
|
|
38
|
+
# Resources
|
|
39
|
+
require_relative "rbac/resources/role"
|
|
40
|
+
require_relative "rbac/resources/entitlement"
|
|
41
|
+
require_relative "rbac/resources/application"
|
|
42
|
+
|
|
43
|
+
@loaded = true
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Register all RBAC resources with the Scimitar engine.
|
|
47
|
+
# Called automatically by the Rails engine, or can be called manually.
|
|
48
|
+
def self.register_resources!
|
|
49
|
+
load!
|
|
50
|
+
|
|
51
|
+
[
|
|
52
|
+
Scimitar::Resources::Rbac::Role,
|
|
53
|
+
Scimitar::Resources::Rbac::Entitlement,
|
|
54
|
+
Scimitar::Resources::Rbac::Application
|
|
55
|
+
].each do |resource|
|
|
56
|
+
unless Scimitar::Engine.custom_resources.include?(resource)
|
|
57
|
+
Scimitar::Engine.add_custom_resource(resource)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Reset load state — useful for testing
|
|
63
|
+
# @api private
|
|
64
|
+
def self.reset!
|
|
65
|
+
@loaded = false
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Auto-load Engine for Rails integration
|
|
71
|
+
require_relative "rbac/engine" if defined?(Rails::Engine)
|
metadata
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: scimitar-rbac
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Ahmed Gasanov
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: scimitar
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rails
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '7.0'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '9.0'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '7.0'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '9.0'
|
|
46
|
+
description: |
|
|
47
|
+
Extends the scimitar gem with Role-Based Access Control (RBAC) resources
|
|
48
|
+
for SCIM v2, based on the NIST RBAC standard and the Baumer et al. research
|
|
49
|
+
paper "SCIM: Survey and Enhancement With RBAC" (IEEE Access, 2023).
|
|
50
|
+
Provides Role, Entitlement, and Application SCIM resource types with full
|
|
51
|
+
schema definitions, enabling standardized RBAC data exchange via SCIM.
|
|
52
|
+
email:
|
|
53
|
+
- spryffee@gmail.com
|
|
54
|
+
executables: []
|
|
55
|
+
extensions: []
|
|
56
|
+
extra_rdoc_files: []
|
|
57
|
+
files:
|
|
58
|
+
- README.md
|
|
59
|
+
- Rakefile
|
|
60
|
+
- lib/generators/scimitar_rbac/install_generator.rb
|
|
61
|
+
- lib/generators/scimitar_rbac/templates/application_model.rb.erb
|
|
62
|
+
- lib/generators/scimitar_rbac/templates/applications_controller.rb.erb
|
|
63
|
+
- lib/generators/scimitar_rbac/templates/entitlement_model.rb.erb
|
|
64
|
+
- lib/generators/scimitar_rbac/templates/entitlements_controller.rb.erb
|
|
65
|
+
- lib/generators/scimitar_rbac/templates/migration.rb.erb
|
|
66
|
+
- lib/generators/scimitar_rbac/templates/role_model.rb.erb
|
|
67
|
+
- lib/generators/scimitar_rbac/templates/roles_controller.rb.erb
|
|
68
|
+
- lib/scimitar/rbac.rb
|
|
69
|
+
- lib/scimitar/rbac/complex_types/application_reference.rb
|
|
70
|
+
- lib/scimitar/rbac/complex_types/entitlement_assignment.rb
|
|
71
|
+
- lib/scimitar/rbac/complex_types/hierarchy_member.rb
|
|
72
|
+
- lib/scimitar/rbac/complex_types/role_assignment.rb
|
|
73
|
+
- lib/scimitar/rbac/engine.rb
|
|
74
|
+
- lib/scimitar/rbac/resources/application.rb
|
|
75
|
+
- lib/scimitar/rbac/resources/entitlement.rb
|
|
76
|
+
- lib/scimitar/rbac/resources/role.rb
|
|
77
|
+
- lib/scimitar/rbac/route_helper.rb
|
|
78
|
+
- lib/scimitar/rbac/schema/application.rb
|
|
79
|
+
- lib/scimitar/rbac/schema/application_reference.rb
|
|
80
|
+
- lib/scimitar/rbac/schema/entitlement.rb
|
|
81
|
+
- lib/scimitar/rbac/schema/entitlement_assignment.rb
|
|
82
|
+
- lib/scimitar/rbac/schema/hierarchy_member.rb
|
|
83
|
+
- lib/scimitar/rbac/schema/role.rb
|
|
84
|
+
- lib/scimitar/rbac/schema/role_assignment.rb
|
|
85
|
+
- lib/scimitar/rbac/version.rb
|
|
86
|
+
homepage: https://github.com/spryffee/scimitar-rbac
|
|
87
|
+
licenses:
|
|
88
|
+
- MIT
|
|
89
|
+
metadata:
|
|
90
|
+
homepage_uri: https://github.com/spryffee/scimitar-rbac
|
|
91
|
+
source_code_uri: https://github.com/spryffee/scimitar-rbac
|
|
92
|
+
rdoc_options: []
|
|
93
|
+
require_paths:
|
|
94
|
+
- lib
|
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
96
|
+
requirements:
|
|
97
|
+
- - ">="
|
|
98
|
+
- !ruby/object:Gem::Version
|
|
99
|
+
version: 3.2.0
|
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - ">="
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '0'
|
|
105
|
+
requirements: []
|
|
106
|
+
rubygems_version: 3.6.9
|
|
107
|
+
specification_version: 4
|
|
108
|
+
summary: RBAC profile for SCIM, built on top of the scimitar gem.
|
|
109
|
+
test_files: []
|