keycloak-admin 1.2.0 → 2.0.1
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 +4 -4
- data/.github/workflows/ci.yml +4 -2
- data/.github/workflows/release.yml +112 -0
- data/CHANGELOG.md +14 -0
- data/Gemfile.lock +19 -20
- data/README.md +25 -5
- data/Rakefile +6 -0
- data/keycloak-admin.gemspec +5 -3
- data/lib/keycloak-admin/client/attack_detection_client.rb +3 -3
- data/lib/keycloak-admin/client/client.rb +18 -7
- data/lib/keycloak-admin/client/client_authz_permission_client.rb +7 -7
- data/lib/keycloak-admin/client/client_authz_policy_client.rb +5 -5
- data/lib/keycloak-admin/client/client_authz_resource_client.rb +6 -6
- data/lib/keycloak-admin/client/client_authz_scope_client.rb +5 -5
- data/lib/keycloak-admin/client/client_client.rb +6 -6
- data/lib/keycloak-admin/client/client_role_client.rb +1 -1
- data/lib/keycloak-admin/client/client_role_mappings_client.rb +2 -2
- data/lib/keycloak-admin/client/client_scope_client.rb +5 -5
- data/lib/keycloak-admin/client/client_scope_protocol_mapper_client.rb +5 -5
- data/lib/keycloak-admin/client/configurable_token_client.rb +4 -3
- data/lib/keycloak-admin/client/group_client.rb +15 -14
- data/lib/keycloak-admin/client/identity_provider_client.rb +4 -4
- data/lib/keycloak-admin/client/organization_client.rb +18 -18
- data/lib/keycloak-admin/client/realm_client.rb +4 -4
- data/lib/keycloak-admin/client/resource.rb +98 -0
- data/lib/keycloak-admin/client/response.rb +36 -0
- data/lib/keycloak-admin/client/role_client.rb +6 -6
- data/lib/keycloak-admin/client/role_mapper_client.rb +9 -8
- data/lib/keycloak-admin/client/token_client.rb +1 -3
- data/lib/keycloak-admin/client/user_client.rb +38 -31
- data/lib/keycloak-admin/configuration.rb +23 -1
- data/lib/keycloak-admin/version.rb +1 -1
- data/lib/keycloak-admin.rb +3 -1
- data/spec/client/attack_detection_client_spec.rb +3 -3
- data/spec/client/client_authz_permission_client_spec.rb +5 -5
- data/spec/client/client_authz_policy_client_spec.rb +5 -5
- data/spec/client/client_authz_resource_client_spec.rb +7 -7
- data/spec/client/client_authz_scope_client_spec.rb +5 -5
- data/spec/client/client_client_spec.rb +14 -14
- data/spec/client/client_role_mappings_client_spec.rb +11 -10
- data/spec/client/client_scope_client_spec.rb +7 -7
- data/spec/client/client_scope_protocol_mapper_client_spec.rb +7 -7
- data/spec/client/client_spec.rb +80 -7
- data/spec/client/group_client_spec.rb +47 -47
- data/spec/client/identity_provider_client_spec.rb +5 -5
- data/spec/client/organization_client_spec.rb +31 -31
- data/spec/client/realm_client_spec.rb +20 -20
- data/spec/client/resource_spec.rb +223 -0
- data/spec/client/response_spec.rb +68 -0
- data/spec/client/role_client_spec.rb +10 -10
- data/spec/client/role_mapper_client_spec.rb +10 -10
- data/spec/client/token_client_spec.rb +5 -5
- data/spec/client/user_client_spec.rb +29 -29
- data/spec/configuration_spec.rb +39 -2
- data/spec/gemspec_spec.rb +25 -0
- data/spec/integration/client_authorization_spec.rb +4 -4
- data/spec/representation/client_authz_policy_representation_spec.rb +1 -1
- data/spec/spec_helper.rb +8 -9
- metadata +42 -7
|
@@ -8,14 +8,14 @@ module KeycloakAdmin
|
|
|
8
8
|
|
|
9
9
|
def list_available
|
|
10
10
|
response = execute_http do
|
|
11
|
-
|
|
11
|
+
resource(list_available_url).get(headers)
|
|
12
12
|
end
|
|
13
13
|
JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def save(role_representation_list)
|
|
17
17
|
execute_http do
|
|
18
|
-
|
|
18
|
+
resource(base_url).post(
|
|
19
19
|
create_payload(role_representation_list), headers
|
|
20
20
|
)
|
|
21
21
|
end
|
|
@@ -10,7 +10,7 @@ module KeycloakAdmin
|
|
|
10
10
|
|
|
11
11
|
def list
|
|
12
12
|
response = execute_http do
|
|
13
|
-
|
|
13
|
+
resource(client_scopes_url).get(headers)
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
JSON.parse(response).map { |h| ClientScopeRepresentation.from_hash(h) }
|
|
@@ -18,7 +18,7 @@ module KeycloakAdmin
|
|
|
18
18
|
|
|
19
19
|
def get(client_scope_id)
|
|
20
20
|
response = execute_http do
|
|
21
|
-
|
|
21
|
+
resource(client_scopes_url(client_scope_id)).get(headers)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
ClientScopeRepresentation.from_hash(JSON.parse(response))
|
|
@@ -26,7 +26,7 @@ module KeycloakAdmin
|
|
|
26
26
|
|
|
27
27
|
def create!(client_scope_representation)
|
|
28
28
|
execute_http do
|
|
29
|
-
|
|
29
|
+
resource(client_scopes_url).post(
|
|
30
30
|
create_payload(client_scope_representation), headers
|
|
31
31
|
)
|
|
32
32
|
end
|
|
@@ -36,7 +36,7 @@ module KeycloakAdmin
|
|
|
36
36
|
|
|
37
37
|
def save(client_scope_representation)
|
|
38
38
|
execute_http do
|
|
39
|
-
|
|
39
|
+
resource(client_scopes_url(client_scope_representation.id)).put(
|
|
40
40
|
create_payload(client_scope_representation), headers
|
|
41
41
|
)
|
|
42
42
|
end
|
|
@@ -50,7 +50,7 @@ module KeycloakAdmin
|
|
|
50
50
|
|
|
51
51
|
def delete(client_scope_id)
|
|
52
52
|
execute_http do
|
|
53
|
-
|
|
53
|
+
resource(client_scopes_url(client_scope_id)).delete(headers)
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
true
|
|
@@ -11,7 +11,7 @@ module KeycloakAdmin
|
|
|
11
11
|
|
|
12
12
|
def list
|
|
13
13
|
response = execute_http do
|
|
14
|
-
|
|
14
|
+
resource(protocol_mappers_url).get(headers)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
JSON.parse(response).map { |h| ProtocolMapperRepresentation.from_hash(h) }
|
|
@@ -19,7 +19,7 @@ module KeycloakAdmin
|
|
|
19
19
|
|
|
20
20
|
def get(mapper_id)
|
|
21
21
|
response = execute_http do
|
|
22
|
-
|
|
22
|
+
resource(protocol_mappers_url(mapper_id)).get(headers)
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
ProtocolMapperRepresentation.from_hash(JSON.parse(response))
|
|
@@ -27,7 +27,7 @@ module KeycloakAdmin
|
|
|
27
27
|
|
|
28
28
|
def create!(mapper_representation)
|
|
29
29
|
execute_http do
|
|
30
|
-
|
|
30
|
+
resource(protocol_mappers_url).post(
|
|
31
31
|
create_payload(mapper_representation), headers
|
|
32
32
|
)
|
|
33
33
|
end
|
|
@@ -37,7 +37,7 @@ module KeycloakAdmin
|
|
|
37
37
|
|
|
38
38
|
def save(mapper_representation)
|
|
39
39
|
execute_http do
|
|
40
|
-
|
|
40
|
+
resource(protocol_mappers_url(mapper_representation.id)).put(
|
|
41
41
|
create_payload(mapper_representation), headers
|
|
42
42
|
)
|
|
43
43
|
end
|
|
@@ -47,7 +47,7 @@ module KeycloakAdmin
|
|
|
47
47
|
|
|
48
48
|
def delete(mapper_id)
|
|
49
49
|
execute_http do
|
|
50
|
-
|
|
50
|
+
resource(protocol_mappers_url(mapper_id)).delete(headers)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
true
|
|
@@ -16,8 +16,8 @@ module KeycloakAdmin
|
|
|
16
16
|
|
|
17
17
|
def exchange_with(user_access_token, token_lifespan_in_seconds)
|
|
18
18
|
response = execute_http do
|
|
19
|
-
|
|
20
|
-
@configuration.
|
|
19
|
+
Resource.execute(
|
|
20
|
+
@configuration.faraday_options.merge(
|
|
21
21
|
method: :post,
|
|
22
22
|
url: token_url,
|
|
23
23
|
payload: { tokenLifespanInSeconds: token_lifespan_in_seconds }.to_json,
|
|
@@ -25,7 +25,8 @@ module KeycloakAdmin
|
|
|
25
25
|
Authorization: "Bearer #{user_access_token}",
|
|
26
26
|
content_type: :json,
|
|
27
27
|
accept: :json
|
|
28
|
-
}
|
|
28
|
+
},
|
|
29
|
+
logger: @configuration.logger
|
|
29
30
|
)
|
|
30
31
|
)
|
|
31
32
|
end
|
|
@@ -8,7 +8,7 @@ module KeycloakAdmin
|
|
|
8
8
|
|
|
9
9
|
def get(group_id)
|
|
10
10
|
response = execute_http do
|
|
11
|
-
|
|
11
|
+
resource(groups_url(group_id)).get(headers)
|
|
12
12
|
end
|
|
13
13
|
GroupRepresentation.from_hash(JSON.parse(response))
|
|
14
14
|
end
|
|
@@ -16,7 +16,7 @@ module KeycloakAdmin
|
|
|
16
16
|
def children(parent_id)
|
|
17
17
|
response = execute_http do
|
|
18
18
|
url = "#{groups_url(parent_id)}/children"
|
|
19
|
-
|
|
19
|
+
resource(url).get(headers)
|
|
20
20
|
end
|
|
21
21
|
JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
|
|
22
22
|
end
|
|
@@ -35,7 +35,7 @@ module KeycloakAdmin
|
|
|
35
35
|
headers
|
|
36
36
|
end
|
|
37
37
|
response = execute_http do
|
|
38
|
-
|
|
38
|
+
resource(groups_url).get(derived_headers)
|
|
39
39
|
end
|
|
40
40
|
JSON.parse(response).map { |group_as_hash| GroupRepresentation.from_hash(group_as_hash) }
|
|
41
41
|
end
|
|
@@ -49,9 +49,9 @@ module KeycloakAdmin
|
|
|
49
49
|
execute_http do
|
|
50
50
|
payload = create_payload(group_representation)
|
|
51
51
|
if group_representation.id
|
|
52
|
-
|
|
52
|
+
resource(groups_url(group_representation.id)).put(payload, headers)
|
|
53
53
|
else
|
|
54
|
-
|
|
54
|
+
resource(groups_url).post(payload, headers)
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
end
|
|
@@ -59,7 +59,7 @@ module KeycloakAdmin
|
|
|
59
59
|
def create_subgroup!(parent_id, name, attributes = {})
|
|
60
60
|
url = "#{groups_url(parent_id)}/children"
|
|
61
61
|
response = execute_http do
|
|
62
|
-
|
|
62
|
+
resource(url).post(
|
|
63
63
|
create_payload(build(name, nil, attributes)), headers
|
|
64
64
|
)
|
|
65
65
|
end
|
|
@@ -68,7 +68,7 @@ module KeycloakAdmin
|
|
|
68
68
|
|
|
69
69
|
def delete(group_id)
|
|
70
70
|
execute_http do
|
|
71
|
-
|
|
71
|
+
resource(groups_url(group_id)).delete(headers)
|
|
72
72
|
end
|
|
73
73
|
true
|
|
74
74
|
end
|
|
@@ -81,7 +81,7 @@ module KeycloakAdmin
|
|
|
81
81
|
url = "#{url}?#{query_string}"
|
|
82
82
|
end
|
|
83
83
|
response = execute_http do
|
|
84
|
-
|
|
84
|
+
resource(url).get(headers)
|
|
85
85
|
end
|
|
86
86
|
JSON.parse(response).map { |user_as_hash| UserRepresentation.from_hash(user_as_hash) }
|
|
87
87
|
end
|
|
@@ -90,7 +90,7 @@ module KeycloakAdmin
|
|
|
90
90
|
def get_realm_level_roles(group_id)
|
|
91
91
|
url = "#{groups_url(group_id)}/role-mappings/realm"
|
|
92
92
|
response = execute_http do
|
|
93
|
-
|
|
93
|
+
resource(url).get(headers)
|
|
94
94
|
end
|
|
95
95
|
JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
|
|
96
96
|
end
|
|
@@ -101,7 +101,7 @@ module KeycloakAdmin
|
|
|
101
101
|
role_representation = RoleClient.new(@configuration, @realm_client).get(role_name)
|
|
102
102
|
url = "#{groups_url(group_id)}/role-mappings/realm"
|
|
103
103
|
response = execute_http do
|
|
104
|
-
|
|
104
|
+
resource(url).post(
|
|
105
105
|
create_payload([role_representation]), headers
|
|
106
106
|
)
|
|
107
107
|
end
|
|
@@ -113,12 +113,13 @@ module KeycloakAdmin
|
|
|
113
113
|
role_representation = RoleClient.new(@configuration, @realm_client).get(role_name)
|
|
114
114
|
url = "#{groups_url(group_id)}/role-mappings/realm"
|
|
115
115
|
execute_http do
|
|
116
|
-
|
|
117
|
-
@configuration.
|
|
118
|
-
url
|
|
116
|
+
Resource.execute(
|
|
117
|
+
@configuration.faraday_options.merge(
|
|
118
|
+
url: url,
|
|
119
119
|
method: :delete,
|
|
120
120
|
payload: create_payload([role_representation]),
|
|
121
|
-
headers: headers
|
|
121
|
+
headers: headers,
|
|
122
|
+
logger: @configuration.logger
|
|
122
123
|
)
|
|
123
124
|
)
|
|
124
125
|
end
|
|
@@ -8,7 +8,7 @@ module KeycloakAdmin
|
|
|
8
8
|
|
|
9
9
|
def create(identity_provider_representation)
|
|
10
10
|
execute_http do
|
|
11
|
-
|
|
11
|
+
resource(identity_providers_url).post(
|
|
12
12
|
create_payload(identity_provider_representation), headers
|
|
13
13
|
)
|
|
14
14
|
end
|
|
@@ -16,7 +16,7 @@ module KeycloakAdmin
|
|
|
16
16
|
|
|
17
17
|
def add_mapping(identity_provider_alias, identity_provider_mapping_representation)
|
|
18
18
|
execute_http do
|
|
19
|
-
|
|
19
|
+
resource(identity_provider_mappers_url(identity_provider_alias)).post(
|
|
20
20
|
create_payload(identity_provider_mapping_representation), headers
|
|
21
21
|
)
|
|
22
22
|
end
|
|
@@ -24,14 +24,14 @@ module KeycloakAdmin
|
|
|
24
24
|
|
|
25
25
|
def list
|
|
26
26
|
response = execute_http do
|
|
27
|
-
|
|
27
|
+
resource(identity_providers_url).get(headers)
|
|
28
28
|
end
|
|
29
29
|
JSON.parse(response).map { |provider_as_hash| IdentityProviderRepresentation.from_hash(provider_as_hash) }
|
|
30
30
|
end
|
|
31
31
|
|
|
32
32
|
def get(internal_id_or_alias=nil)
|
|
33
33
|
response = execute_http do
|
|
34
|
-
|
|
34
|
+
resource(identity_providers_url(internal_id_or_alias)).get(headers)
|
|
35
35
|
end
|
|
36
36
|
IdentityProviderRepresentation.from_hash(JSON.parse(response))
|
|
37
37
|
end
|
|
@@ -9,28 +9,28 @@ module KeycloakAdmin
|
|
|
9
9
|
# This endpoint does not return members
|
|
10
10
|
def list(brief_representation=true, exact=nil, first=nil, max=nil, query=nil, search=nil)
|
|
11
11
|
response = execute_http do
|
|
12
|
-
|
|
12
|
+
resource(organizations_url_with_parameters(brief_representation, exact, first, max, query, search)).get(headers)
|
|
13
13
|
end
|
|
14
14
|
JSON.parse(response).map { |organization_as_hash| OrganizationRepresentation.from_hash(organization_as_hash) }
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def count(exact=nil, query=nil, search=nil)
|
|
18
18
|
response = execute_http do
|
|
19
|
-
|
|
19
|
+
resource(count_url(exact, query, search)).get(headers)
|
|
20
20
|
end
|
|
21
21
|
response.to_i
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def delete(organization_id)
|
|
25
25
|
execute_http do
|
|
26
|
-
|
|
26
|
+
resource(organization_url(organization_id)).delete(headers)
|
|
27
27
|
end
|
|
28
28
|
true
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def update(organization_representation)
|
|
32
32
|
execute_http do
|
|
33
|
-
|
|
33
|
+
resource(organization_url(organization_representation.id)).put(
|
|
34
34
|
create_payload(organization_representation), headers
|
|
35
35
|
)
|
|
36
36
|
end
|
|
@@ -45,7 +45,7 @@ module KeycloakAdmin
|
|
|
45
45
|
# This operation does not associate members and identity providers
|
|
46
46
|
def save(organization_representation)
|
|
47
47
|
execute_http do
|
|
48
|
-
|
|
48
|
+
resource(organizations_url).post(
|
|
49
49
|
create_payload(organization_representation), headers
|
|
50
50
|
)
|
|
51
51
|
end
|
|
@@ -54,14 +54,14 @@ module KeycloakAdmin
|
|
|
54
54
|
|
|
55
55
|
def get(organization_id)
|
|
56
56
|
response = execute_http do
|
|
57
|
-
|
|
57
|
+
resource(organization_url(organization_id)).get(headers)
|
|
58
58
|
end
|
|
59
59
|
OrganizationRepresentation.from_hash(JSON.parse(response))
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def identity_providers(organization_id)
|
|
63
63
|
response = execute_http do
|
|
64
|
-
|
|
64
|
+
resource(identity_providers_url(organization_id)).get(headers)
|
|
65
65
|
end
|
|
66
66
|
JSON.parse(response).map { |idp_as_hash| IdentityProviderRepresentation.from_hash(idp_as_hash) }
|
|
67
67
|
end
|
|
@@ -69,7 +69,7 @@ module KeycloakAdmin
|
|
|
69
69
|
def get_identity_provider(organization_id, identity_provider_alias)
|
|
70
70
|
raise ArgumentError.new("identity_provider_alias must be defined") if identity_provider_alias.nil?
|
|
71
71
|
response = execute_http do
|
|
72
|
-
|
|
72
|
+
resource("#{identity_providers_url(organization_id)}/#{identity_provider_alias}").get(headers)
|
|
73
73
|
end
|
|
74
74
|
IdentityProviderRepresentation.from_hash(JSON.parse(response))
|
|
75
75
|
end
|
|
@@ -77,28 +77,28 @@ module KeycloakAdmin
|
|
|
77
77
|
def add_identity_provider(organization_id, identity_provider_alias)
|
|
78
78
|
raise ArgumentError.new("identity_provider_alias must be defined") if identity_provider_alias.nil?
|
|
79
79
|
execute_http do
|
|
80
|
-
|
|
80
|
+
resource(identity_providers_url(organization_id)).post(identity_provider_alias, headers)
|
|
81
81
|
end
|
|
82
82
|
true
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def delete_identity_provider(organization_id, identity_provider_alias)
|
|
86
86
|
execute_http do
|
|
87
|
-
|
|
87
|
+
resource(identity_provider_url(organization_id, identity_provider_alias)).delete(headers)
|
|
88
88
|
end
|
|
89
89
|
true
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def members_count(organization_id)
|
|
93
93
|
response = execute_http do
|
|
94
|
-
|
|
94
|
+
resource(members_count_url(organization_id)).get(headers)
|
|
95
95
|
end
|
|
96
96
|
response.to_i
|
|
97
97
|
end
|
|
98
98
|
|
|
99
99
|
def members(organization_id, exact=nil, first=nil, max=nil, membership_type=nil, search=nil)
|
|
100
100
|
response = execute_http do
|
|
101
|
-
|
|
101
|
+
resource(members_url_with_query_parameters(organization_id, exact, first, max, membership_type, search)).get(headers)
|
|
102
102
|
end
|
|
103
103
|
JSON.parse(response).map { |member_as_hash| MemberRepresentation.from_hash(member_as_hash) }
|
|
104
104
|
end
|
|
@@ -106,14 +106,14 @@ module KeycloakAdmin
|
|
|
106
106
|
def invite_existing_user(organization_id, user_id)
|
|
107
107
|
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
108
108
|
execute_http do
|
|
109
|
-
|
|
109
|
+
resource(invite_existing_user_url(organization_id)).post({id: user_id}, headers.merge(content_type: "application/x-www-form-urlencoded"))
|
|
110
110
|
end
|
|
111
111
|
true
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def invite_user(organization_id, email, first_name, last_name)
|
|
115
115
|
execute_http do
|
|
116
|
-
|
|
116
|
+
resource(invite_user_url(organization_id)).post({
|
|
117
117
|
email: email,
|
|
118
118
|
firstName: first_name,
|
|
119
119
|
lastName: last_name
|
|
@@ -125,28 +125,28 @@ module KeycloakAdmin
|
|
|
125
125
|
def add_member(organization_id, user_id)
|
|
126
126
|
raise ArgumentError.new("user_id must be defined") if user_id.nil?
|
|
127
127
|
execute_http do
|
|
128
|
-
|
|
128
|
+
resource(members_url(organization_id)).post(user_id, headers)
|
|
129
129
|
end
|
|
130
130
|
true
|
|
131
131
|
end
|
|
132
132
|
|
|
133
133
|
def delete_member(organization_id, member_id)
|
|
134
134
|
execute_http do
|
|
135
|
-
|
|
135
|
+
resource(member_url(organization_id, member_id)).delete(headers)
|
|
136
136
|
end
|
|
137
137
|
true
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
def get_member(organization_id, member_id)
|
|
141
141
|
response = execute_http do
|
|
142
|
-
|
|
142
|
+
resource(member_url(organization_id, member_id)).get(headers)
|
|
143
143
|
end
|
|
144
144
|
MemberRepresentation.from_hash(JSON.parse(response))
|
|
145
145
|
end
|
|
146
146
|
|
|
147
147
|
def associated_with_member(member_id, brief_representation=true)
|
|
148
148
|
response = execute_http do
|
|
149
|
-
|
|
149
|
+
resource(associated_with_member_url(member_id, brief_representation)).get(headers)
|
|
150
150
|
end
|
|
151
151
|
JSON.parse(response).map { |organization_as_hash| OrganizationRepresentation.from_hash(organization_as_hash) }
|
|
152
152
|
end
|
|
@@ -7,21 +7,21 @@ module KeycloakAdmin
|
|
|
7
7
|
|
|
8
8
|
def list
|
|
9
9
|
response = execute_http do
|
|
10
|
-
|
|
10
|
+
resource(realm_list_url).get(headers)
|
|
11
11
|
end
|
|
12
12
|
JSON.parse(response).map { |realm_as_hash| RealmRepresentation.from_hash(realm_as_hash) }
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def delete
|
|
16
16
|
execute_http do
|
|
17
|
-
|
|
17
|
+
resource(realm_admin_url).delete(headers)
|
|
18
18
|
end
|
|
19
19
|
true
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
def save(realm_representation)
|
|
23
23
|
execute_http do
|
|
24
|
-
|
|
24
|
+
resource(realm_list_url).post(
|
|
25
25
|
create_payload(realm_representation), headers
|
|
26
26
|
)
|
|
27
27
|
end
|
|
@@ -29,7 +29,7 @@ module KeycloakAdmin
|
|
|
29
29
|
|
|
30
30
|
def update(realm_representation_body)
|
|
31
31
|
execute_http do
|
|
32
|
-
|
|
32
|
+
resource(realm_admin_url).put(
|
|
33
33
|
create_payload(realm_representation_body), headers
|
|
34
34
|
)
|
|
35
35
|
end
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
require "faraday"
|
|
2
|
+
require "uri"
|
|
3
|
+
|
|
4
|
+
module KeycloakAdmin
|
|
5
|
+
# Thin facade over Faraday that mirrors RestClient::Resource/RestClient::Request's call
|
|
6
|
+
# shape, so every client class can build/send requests without depending on Faraday's API
|
|
7
|
+
# directly.
|
|
8
|
+
class Resource
|
|
9
|
+
MIME_TYPES = { json: "application/json" }.freeze
|
|
10
|
+
|
|
11
|
+
def self.execute(options)
|
|
12
|
+
options = options.dup
|
|
13
|
+
method = options.delete(:method)
|
|
14
|
+
url = options.delete(:url)
|
|
15
|
+
payload = options.delete(:payload)
|
|
16
|
+
headers = options.delete(:headers) || {}
|
|
17
|
+
logger = options.delete(:logger)
|
|
18
|
+
new(url, options, logger).send(:request, method, payload, headers)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.put(url, payload, headers = {}, logger = nil)
|
|
22
|
+
new(url, {}, logger).put(payload, headers)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def initialize(url, options = {}, logger = nil)
|
|
26
|
+
@url = url
|
|
27
|
+
@options = options || {}
|
|
28
|
+
@logger = logger
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def get(headers = {})
|
|
32
|
+
request(:get, nil, headers)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def delete(headers = {})
|
|
36
|
+
request(:delete, nil, headers)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def post(payload, headers = {})
|
|
40
|
+
request(:post, payload, headers)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def put(payload, headers = {})
|
|
44
|
+
request(:put, payload, headers)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def request(method, payload, headers)
|
|
50
|
+
headers = headers || {}
|
|
51
|
+
query = headers[:params]
|
|
52
|
+
send_headers = build_headers(headers.reject { |key, _| key == :params })
|
|
53
|
+
body = encode_payload(payload)
|
|
54
|
+
|
|
55
|
+
# RestClient::Payload::UrlEncoded always stamped this Content-Type for Hash payloads,
|
|
56
|
+
# regardless of what the caller's headers said; token retrieval relies on it and never
|
|
57
|
+
# sets content_type itself.
|
|
58
|
+
if payload.is_a?(Hash) && !send_headers.key?("Content-Type")
|
|
59
|
+
send_headers["Content-Type"] = "application/x-www-form-urlencoded"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
faraday_response = connection.public_send(method) do |req|
|
|
63
|
+
req.params.update(query) if query
|
|
64
|
+
send_headers.each { |name, value| req.headers[name] = value }
|
|
65
|
+
req.body = body unless body.nil?
|
|
66
|
+
end
|
|
67
|
+
Response.new(faraday_response)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def connection
|
|
71
|
+
Faraday.new(url: @url, **@options) do |f|
|
|
72
|
+
f.response :raise_error
|
|
73
|
+
# Added after :raise_error so it logs the raw response (status/duration) before
|
|
74
|
+
# :raise_error can raise, including on failures. Faraday's logger middleware logs
|
|
75
|
+
# headers by default, which would print the bearer token on every call; headers: false
|
|
76
|
+
# keeps this to method/url/status only. Bodies are already off by Faraday's own default.
|
|
77
|
+
f.response :logger, @logger, headers: false if @logger
|
|
78
|
+
f.adapter Faraday.default_adapter
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def encode_payload(payload)
|
|
83
|
+
return nil if payload.nil?
|
|
84
|
+
payload.is_a?(Hash) ? URI.encode_www_form(payload) : payload.to_s
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def build_headers(headers)
|
|
88
|
+
headers.each_with_object({}) do |(key, value), result|
|
|
89
|
+
name = key.is_a?(Symbol) ? humanize(key) : key.to_s
|
|
90
|
+
result[name] = value.is_a?(Symbol) ? MIME_TYPES.fetch(value, value.to_s) : value
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def humanize(key)
|
|
95
|
+
key.to_s.split("_").map { |word| word.capitalize }.join("-")
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module KeycloakAdmin
|
|
2
|
+
# Wraps a Faraday::Response the way RestClient::Response used to: as the body string
|
|
3
|
+
# itself (RestClient::Response < String), so existing call sites that do JSON.parse(response)
|
|
4
|
+
# or response.to_i keep working, while .body/.headers/.status/.code/.reason_phrase stay
|
|
5
|
+
# available for call sites that need response metadata.
|
|
6
|
+
class Response < String
|
|
7
|
+
attr_reader :status, :reason_phrase, :headers
|
|
8
|
+
|
|
9
|
+
def initialize(faraday_response)
|
|
10
|
+
super(faraday_response.body.to_s)
|
|
11
|
+
@status = faraday_response.status
|
|
12
|
+
@reason_phrase = faraday_response.reason_phrase
|
|
13
|
+
@headers = symbolize_headers(faraday_response.headers)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
alias_method :code, :status
|
|
17
|
+
alias_method :body, :to_s
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def symbolize_headers(faraday_headers)
|
|
22
|
+
faraday_headers.each_with_object({}) do |(key, value), result|
|
|
23
|
+
symbol_key = key.downcase.tr("-", "_").to_sym
|
|
24
|
+
result[symbol_key] = symbol_key == :set_cookie ? split_set_cookie(value) : value
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Net::HTTP (used by Faraday's default adapter) joins repeated Set-Cookie header lines with
|
|
29
|
+
# ", " (see Net::HTTPHeader#each_header), losing the one-header-per-cookie structure RestClient
|
|
30
|
+
# exposed as an Array. Re-split on cookie boundaries rather than on every comma, since a comma
|
|
31
|
+
# can also appear inside a cookie's own "Expires=Wed, 09 Jun 2027 ..." attribute.
|
|
32
|
+
def split_set_cookie(value)
|
|
33
|
+
value.split(/,(?=\s*[^;\s]+=)/).map(&:strip)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -8,17 +8,17 @@ module KeycloakAdmin
|
|
|
8
8
|
|
|
9
9
|
def list
|
|
10
10
|
response = execute_http do
|
|
11
|
-
|
|
11
|
+
resource(roles_url).get(headers)
|
|
12
12
|
end
|
|
13
13
|
JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
|
|
14
14
|
end
|
|
15
|
-
|
|
15
|
+
|
|
16
16
|
# Returns the role representation for the specified role name
|
|
17
17
|
def get(name)
|
|
18
18
|
# allows special characters in the name like space
|
|
19
19
|
name = URI.encode_uri_component(name)
|
|
20
20
|
response = execute_http do
|
|
21
|
-
|
|
21
|
+
resource(role_name_url(name)).get(headers)
|
|
22
22
|
end
|
|
23
23
|
RoleRepresentation.from_hash JSON.parse(response)
|
|
24
24
|
end
|
|
@@ -28,7 +28,7 @@ module KeycloakAdmin
|
|
|
28
28
|
# allows special characters in the name like space
|
|
29
29
|
name = URI.encode_uri_component(name)
|
|
30
30
|
response = execute_http do
|
|
31
|
-
|
|
31
|
+
resource("#{role_name_url(name)}/groups").get(headers)
|
|
32
32
|
end
|
|
33
33
|
JSON.parse(response).map { |role_as_hash| GroupRepresentation.from_hash(role_as_hash) }
|
|
34
34
|
end
|
|
@@ -37,9 +37,9 @@ module KeycloakAdmin
|
|
|
37
37
|
execute_http do
|
|
38
38
|
payload = create_payload(role_representation)
|
|
39
39
|
if role_representation.id
|
|
40
|
-
|
|
40
|
+
resource(role_id_url(role_representation.id)).put(payload, headers)
|
|
41
41
|
else
|
|
42
|
-
|
|
42
|
+
resource(roles_url).post(payload, headers)
|
|
43
43
|
end
|
|
44
44
|
end
|
|
45
45
|
end
|
|
@@ -7,14 +7,14 @@ module KeycloakAdmin
|
|
|
7
7
|
|
|
8
8
|
def list
|
|
9
9
|
response = execute_http do
|
|
10
|
-
|
|
10
|
+
resource(realm_level_url).get(headers)
|
|
11
11
|
end
|
|
12
12
|
JSON.parse(response).map { |role_as_hash| RoleRepresentation.from_hash(role_as_hash) }
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def save_realm_level(role_representation_list)
|
|
16
16
|
execute_http do
|
|
17
|
-
|
|
17
|
+
resource(realm_level_url).post(
|
|
18
18
|
create_payload(role_representation_list), headers
|
|
19
19
|
)
|
|
20
20
|
end
|
|
@@ -22,20 +22,21 @@ module KeycloakAdmin
|
|
|
22
22
|
|
|
23
23
|
def remove_realm_level(role_representation_list)
|
|
24
24
|
execute_http do
|
|
25
|
-
|
|
26
|
-
@configuration.
|
|
25
|
+
Resource.execute(
|
|
26
|
+
@configuration.faraday_options.merge(
|
|
27
27
|
method: :delete,
|
|
28
28
|
url: realm_level_url,
|
|
29
|
-
payload: create_payload(role_representation_list),
|
|
30
|
-
headers: headers
|
|
29
|
+
payload: create_payload(role_representation_list),
|
|
30
|
+
headers: headers,
|
|
31
|
+
logger: @configuration.logger
|
|
31
32
|
)
|
|
32
33
|
)
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
def remove_all_realm_roles
|
|
37
38
|
execute_http do
|
|
38
|
-
|
|
39
|
+
resource(realm_level_url).delete(headers)
|
|
39
40
|
end
|
|
40
41
|
true
|
|
41
42
|
end
|