code0-identities 0.0.1 → 0.0.2
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/.rubocop.yml +4 -1
- data/.tool-versions +1 -1
- data/README.md +42 -1
- data/lib/code0/identities/provider/base_oauth.rb +5 -2
- data/lib/code0/identities/provider/discord.rb +1 -1
- data/lib/code0/identities/provider/github.rb +1 -1
- data/lib/code0/identities/provider/gitlab.rb +1 -1
- data/lib/code0/identities/provider/google.rb +1 -1
- data/lib/code0/identities/provider/microsoft.rb +1 -1
- data/lib/code0/identities/provider/oidc.rb +65 -0
- data/lib/code0/identities/provider/saml.rb +78 -0
- data/lib/code0/identities/version.rb +1 -1
- data/lib/code0/identities.rb +3 -0
- data/renovate.json +1 -2
- data/sig/code0/identities/provider/oidc.rbs +17 -0
- data/sig/code0/identities/provider/saml.rbs +11 -0
- metadata +40 -12
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c123702886210d5646bd8b5bc8090334e2b4c4cd66a5c6cc2b016f30405d8ec2
|
|
4
|
+
data.tar.gz: 6d0829ce20accd660c8e478c85e3b5600ecd395495887d73266c5cd6070ac852
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 20a9b75f381cc363ff01d094f1058a3564047ea7e7b94bf2ece6314b87867daa64d700eb8b698510089d15786234816de57376a20d72660c3a70bf33e93ea28e
|
|
7
|
+
data.tar.gz: da743a997b959429bc8ec815075aba4a4f920036721de6932567425f4ca35ff3daf36bd09a22eb789892d1acd2e9be67e7419fcfb7bf49bc17b08506020213a7
|
data/.rubocop.yml
CHANGED
|
@@ -3,7 +3,7 @@ require:
|
|
|
3
3
|
- rubocop-rspec
|
|
4
4
|
|
|
5
5
|
AllCops:
|
|
6
|
-
TargetRubyVersion: 3.
|
|
6
|
+
TargetRubyVersion: 3.2.0
|
|
7
7
|
NewCops: enable
|
|
8
8
|
|
|
9
9
|
Gemspec/DevelopmentDependencies:
|
|
@@ -38,6 +38,9 @@ RSpec/ExampleLength:
|
|
|
38
38
|
Style/Documentation:
|
|
39
39
|
Enabled: false
|
|
40
40
|
|
|
41
|
+
Style/HashSyntax:
|
|
42
|
+
EnforcedShorthandSyntax: never
|
|
43
|
+
|
|
41
44
|
RSpec/MultipleMemoizedHelpers:
|
|
42
45
|
Enabled: false
|
|
43
46
|
RSpec/MultipleExpectations:
|
data/.tool-versions
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
ruby 3.
|
|
1
|
+
ruby 3.4.7
|
data/README.md
CHANGED
|
@@ -10,6 +10,8 @@ OAuth:
|
|
|
10
10
|
- Microsoft
|
|
11
11
|
- Github
|
|
12
12
|
- Gitlab
|
|
13
|
+
- OIDC / oAuth2
|
|
14
|
+
- SAML
|
|
13
15
|
|
|
14
16
|
## Installation
|
|
15
17
|
|
|
@@ -91,4 +93,43 @@ def fetch_configuration
|
|
|
91
93
|
}
|
|
92
94
|
end
|
|
93
95
|
|
|
94
|
-
```
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
# Configuration
|
|
99
|
+
|
|
100
|
+
As you already know, we allow / require to pass in a configuration. Here are all avaiable configuration keys:
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Oauth Based:
|
|
104
|
+
Here is the updated table where each key in the JSON (`identifier`, `username`, etc.) is explicitly labeled:
|
|
105
|
+
|
|
106
|
+
| Name | Description | Default |
|
|
107
|
+
|------------------------------------|--------------------------------------------------------------------------------------------|-----------------------------------------------------|
|
|
108
|
+
| `client_id` | The client id of the application (needs to be set) | **(no default specified)** |
|
|
109
|
+
| `client_secret` | The client secret of the application (needs to be set) | **(no default specified)** |
|
|
110
|
+
| `redirect_uri` | The redirect URL of the application (needs to be set) | **(no default specified)** |
|
|
111
|
+
| `provider_name` | The provider name (not necessarily) | depends on the provider (e.g., `discord`, `github`) |
|
|
112
|
+
| `user_details_url` | The user details URL to gather user information (only for OIDC) | **(no default specified)** |
|
|
113
|
+
| `authorization_url` | The URL which the user has to access to authorize (only for OIDC) | **(no default specified)** |
|
|
114
|
+
| `attribute_statements` | The keys which the response of the user details has (id, name, email, ...) (only for OIDC) | `{}` (see below for more) |
|
|
115
|
+
| `attribute_statements.identifier` | The identifier of the user to identify (only for OIDC) | `["id", "sub", "identifier"]` |
|
|
116
|
+
| `attribute_statements.username` | The username of the user (only for OIDC) | `["username", "name", "login"]` |
|
|
117
|
+
| `attribute_statements.email` | The email address of the user (only for OIDC) | `["email", "mail"]` |
|
|
118
|
+
| `attribute_statements.firstname` | The first name of the user (only for OIDC) | `["first_name", "firstname", ...]` |
|
|
119
|
+
| `attribute_statements.lastname` | The last name of the user (only for OIDC) | `["last_name", "lastname", ...]` |
|
|
120
|
+
|
|
121
|
+
## SAML
|
|
122
|
+
|
|
123
|
+
| Name | Description | Default |
|
|
124
|
+
|----------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------|
|
|
125
|
+
| `provider_name` | The provider name (not necessarily) | `saml` |
|
|
126
|
+
| `attribute_statements` | The keys which the response of the user details has (id, name, email, ...) (only for OIDC) | `{}` (see below for more) |
|
|
127
|
+
| `attribute_statements.username` | The username of the user | `["username", "name", ...]` |
|
|
128
|
+
| `attribute_statements.email` | The email address of the user | `["email", "mail", ...]` |
|
|
129
|
+
| `attribute_statements.firstname` | The first name of the user | `["first_name", "firstname", ...]` |
|
|
130
|
+
| `attribute_statements.lastname` | The last name of the user | `["last_name", "lastname", ...]` |
|
|
131
|
+
| `settings` | The settings to configure the saml response/requests (see [SAML-Toolkits#L200](https://github.com/SAML-Toolkits/ruby-saml/blob/master/README.md?plain=1#L200)) | `{}` |
|
|
132
|
+
| `response_settings` | The response settings to disable some checks if you want (see [SAML-Toolkits#L234](https://github.com/SAML-Toolkits/ruby-saml/blob/master/README.md?plain=1#L234)) | `{}` |
|
|
133
|
+
| `metadata_url` | The metadata url to fetch the metadatas (replacement for `settings`) | **(no default specified)** |
|
|
134
|
+
|
|
135
|
+
|
|
@@ -68,9 +68,12 @@ module Code0
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def config
|
|
71
|
-
|
|
71
|
+
config = config_loader
|
|
72
|
+
config = config_loader.call if config_loader.is_a?(Proc)
|
|
72
73
|
|
|
73
|
-
|
|
74
|
+
config[:provider_name] ||= self.class.name.downcase.split("::").last
|
|
75
|
+
|
|
76
|
+
config
|
|
74
77
|
end
|
|
75
78
|
end
|
|
76
79
|
end
|
|
@@ -37,7 +37,7 @@ module Code0
|
|
|
37
37
|
username = body["username"]
|
|
38
38
|
email = body["email"]
|
|
39
39
|
|
|
40
|
-
Identity.new(
|
|
40
|
+
Identity.new(config[:provider_name], identifier, username, email, nil, nil)
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
end
|
|
@@ -41,7 +41,7 @@ module Code0
|
|
|
41
41
|
firstname = body["given_name"]
|
|
42
42
|
lastname = body["family_name"]
|
|
43
43
|
|
|
44
|
-
Identity.new(:
|
|
44
|
+
Identity.new(config[:provider_name], identifier, username, email, firstname, lastname)
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Code0
|
|
4
|
+
module Identities
|
|
5
|
+
module Provider
|
|
6
|
+
class Oidc < BaseOauth
|
|
7
|
+
def token_url
|
|
8
|
+
config[:token_url]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def token_payload(code)
|
|
12
|
+
{ code: code,
|
|
13
|
+
grant_type: "authorization_code",
|
|
14
|
+
redirect_uri: config[:redirect_uri],
|
|
15
|
+
client_id: config[:client_id],
|
|
16
|
+
client_secret: config[:client_secret] }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def user_details_url
|
|
20
|
+
config[:user_details_url]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def authorization_url
|
|
24
|
+
config[:authorization_url]
|
|
25
|
+
.gsub("{client_id}", config[:client_id])
|
|
26
|
+
.gsub("{redirect_uri}", config[:redirect_uri])
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def create_identity(response, *)
|
|
30
|
+
body = response.parsed_response
|
|
31
|
+
|
|
32
|
+
Identity.new(config[:provider_name],
|
|
33
|
+
find_attribute(body, config[:attribute_statements][:identifier]),
|
|
34
|
+
find_attribute(body, config[:attribute_statements][:username]),
|
|
35
|
+
find_attribute(body, config[:attribute_statements][:email]),
|
|
36
|
+
find_attribute(body, config[:attribute_statements][:firstname]),
|
|
37
|
+
find_attribute(body, config[:attribute_statements][:lastname]))
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def config
|
|
41
|
+
config = super
|
|
42
|
+
|
|
43
|
+
# rubocop:disable Layout/LineLength
|
|
44
|
+
config[:provider_name] ||= :oidc
|
|
45
|
+
config[:attribute_statements] ||= {}
|
|
46
|
+
config[:attribute_statements][:identifier] ||= %w[sub id identifier]
|
|
47
|
+
config[:attribute_statements][:username] ||= %w[username name login]
|
|
48
|
+
config[:attribute_statements][:email] ||= %w[email mail]
|
|
49
|
+
config[:attribute_statements][:firstname] ||= %w[first_name firstname firstName givenname given_name givenName]
|
|
50
|
+
config[:attribute_statements][:lastname] ||= %w[last_name lastname lastName family_name familyName familyname]
|
|
51
|
+
# rubocop:enable Layout/LineLength
|
|
52
|
+
|
|
53
|
+
config
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def find_attribute(attributes, attribute_statements)
|
|
57
|
+
attribute_statements.each do |statement|
|
|
58
|
+
return attributes[statement] unless attributes[statement].nil?
|
|
59
|
+
end
|
|
60
|
+
nil
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Code0
|
|
4
|
+
module Identities
|
|
5
|
+
module Provider
|
|
6
|
+
class Saml
|
|
7
|
+
attr_reader :config_loader
|
|
8
|
+
|
|
9
|
+
def initialize(config_loader)
|
|
10
|
+
@config_loader = config_loader
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def authorization_url
|
|
14
|
+
request = OneLogin::RubySaml::Authrequest.new
|
|
15
|
+
request.create(create_settings)
|
|
16
|
+
|
|
17
|
+
request.instance_variable_get :@login_url
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def load_identity(**params)
|
|
21
|
+
response = OneLogin::RubySaml::Response.new(params[:SAMLResponse],
|
|
22
|
+
{ **config[:response_settings], settings: create_settings })
|
|
23
|
+
attributes = response.attributes
|
|
24
|
+
|
|
25
|
+
Identity.new(config[:provider_name],
|
|
26
|
+
response.name_id,
|
|
27
|
+
find_attribute(attributes, config[:attribute_statements][:username]),
|
|
28
|
+
find_attribute(attributes, config[:attribute_statements][:email]),
|
|
29
|
+
find_attribute(attributes, config[:attribute_statements][:firstname]),
|
|
30
|
+
find_attribute(attributes, config[:attribute_statements][:lastname]))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def find_attribute(attributes, attribute_statements)
|
|
36
|
+
attribute_statements.each do |statement|
|
|
37
|
+
return attributes[statement] unless attributes[statement].nil?
|
|
38
|
+
end
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def create_settings
|
|
43
|
+
if config[:metadata_url].nil?
|
|
44
|
+
settings = OneLogin::RubySaml::Settings.new
|
|
45
|
+
else
|
|
46
|
+
idp_metadata_parser = OneLogin::RubySaml::IdpMetadataParser.new
|
|
47
|
+
settings = idp_metadata_parser.parse_remote(config[:metadata_url])
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
settings.name_identifier_format = "urn:oasis:names:tc:SAML:2.0:nameid-format:persistent"
|
|
51
|
+
|
|
52
|
+
config[:settings].each do |key, value|
|
|
53
|
+
settings.send(:"#{key}=", value)
|
|
54
|
+
end
|
|
55
|
+
settings
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def config
|
|
59
|
+
config = config_loader
|
|
60
|
+
config = config_loader.call if config_loader.is_a?(Proc)
|
|
61
|
+
|
|
62
|
+
# rubocop:disable Layout/LineLength
|
|
63
|
+
config[:provider_name] ||= :saml
|
|
64
|
+
config[:response_settings] ||= {}
|
|
65
|
+
config[:settings] ||= {}
|
|
66
|
+
config[:attribute_statements] ||= {}
|
|
67
|
+
config[:attribute_statements][:username] ||= %w[username name http://schemas.goauthentik.io/2021/02/saml/username]
|
|
68
|
+
config[:attribute_statements][:email] ||= %w[email mail http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress http://schemas.microsoft.com/ws/2008/06/identity/claims/emailaddress]
|
|
69
|
+
config[:attribute_statements][:firstname] ||= %w[first_name firstname firstName http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname http://schemas.microsoft.com/ws/2008/06/identity/claims/givenname]
|
|
70
|
+
config[:attribute_statements][:lastname] ||= %w[last_name lastname lastName http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname http://schemas.microsoft.com/ws/2008/06/identity/claims/surname]
|
|
71
|
+
# rubocop:enable Layout/LineLength
|
|
72
|
+
|
|
73
|
+
config
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
data/lib/code0/identities.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "httparty"
|
|
4
|
+
require "onelogin/ruby-saml"
|
|
4
5
|
|
|
5
6
|
require_relative "identities/version"
|
|
6
7
|
require_relative "identities/identity_provider"
|
|
@@ -10,6 +11,8 @@ require_relative "identities/provider/microsoft"
|
|
|
10
11
|
require_relative "identities/provider/google"
|
|
11
12
|
require_relative "identities/provider/discord"
|
|
12
13
|
require_relative "identities/provider/github"
|
|
14
|
+
require_relative "identities/provider/oidc"
|
|
15
|
+
require_relative "identities/provider/saml"
|
|
13
16
|
|
|
14
17
|
module Code0
|
|
15
18
|
module Identities
|
data/renovate.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Code0
|
|
2
|
+
module Identities
|
|
3
|
+
module Provider
|
|
4
|
+
class Oidc < BaseOauth
|
|
5
|
+
def token_url: () -> String
|
|
6
|
+
|
|
7
|
+
def token_payload: (code: String) -> { code: String, grant_type: "authorization_code", redirect_uri: String, client_id: String, client_secret: String }
|
|
8
|
+
|
|
9
|
+
def user_details_url: () -> String
|
|
10
|
+
|
|
11
|
+
def authorization_url: () -> String
|
|
12
|
+
|
|
13
|
+
def create_identity: (response: Net::HTTPResponse) -> Identity
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
CHANGED
|
@@ -1,15 +1,28 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: code0-identities
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dario Pranjic
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.3.0
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.3.0
|
|
13
26
|
- !ruby/object:Gem::Dependency
|
|
14
27
|
name: httparty
|
|
15
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -25,19 +38,19 @@ dependencies:
|
|
|
25
38
|
- !ruby/object:Gem::Version
|
|
26
39
|
version: '0.22'
|
|
27
40
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
41
|
+
name: ruby-saml
|
|
29
42
|
requirement: !ruby/object:Gem::Requirement
|
|
30
43
|
requirements:
|
|
31
44
|
- - "~>"
|
|
32
45
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
34
|
-
type: :
|
|
46
|
+
version: '1.18'
|
|
47
|
+
type: :runtime
|
|
35
48
|
prerelease: false
|
|
36
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
50
|
requirements:
|
|
38
51
|
- - "~>"
|
|
39
52
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
53
|
+
version: '1.18'
|
|
41
54
|
- !ruby/object:Gem::Dependency
|
|
42
55
|
name: rake
|
|
43
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -108,7 +121,20 @@ dependencies:
|
|
|
108
121
|
- - "~>"
|
|
109
122
|
- !ruby/object:Gem::Version
|
|
110
123
|
version: '2.29'
|
|
111
|
-
|
|
124
|
+
- !ruby/object:Gem::Dependency
|
|
125
|
+
name: webmock
|
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: 3.23.1
|
|
131
|
+
type: :development
|
|
132
|
+
prerelease: false
|
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - "~>"
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: 3.23.1
|
|
112
138
|
email:
|
|
113
139
|
- dpranjic@code0.tech
|
|
114
140
|
executables: []
|
|
@@ -130,6 +156,8 @@ files:
|
|
|
130
156
|
- lib/code0/identities/provider/gitlab.rb
|
|
131
157
|
- lib/code0/identities/provider/google.rb
|
|
132
158
|
- lib/code0/identities/provider/microsoft.rb
|
|
159
|
+
- lib/code0/identities/provider/oidc.rb
|
|
160
|
+
- lib/code0/identities/provider/saml.rb
|
|
133
161
|
- lib/code0/identities/version.rb
|
|
134
162
|
- renovate.json
|
|
135
163
|
- sig/code0/identities.rbs
|
|
@@ -141,6 +169,8 @@ files:
|
|
|
141
169
|
- sig/code0/identities/provider/gitlab.rbs
|
|
142
170
|
- sig/code0/identities/provider/google.rbs
|
|
143
171
|
- sig/code0/identities/provider/microsoft.rbs
|
|
172
|
+
- sig/code0/identities/provider/oidc.rbs
|
|
173
|
+
- sig/code0/identities/provider/saml.rbs
|
|
144
174
|
- sig/code0/identities/version.rbs
|
|
145
175
|
homepage: https://github.com/code0-tech/code0-identities
|
|
146
176
|
licenses:
|
|
@@ -150,7 +180,6 @@ metadata:
|
|
|
150
180
|
source_code_uri: https://github.com/code0-tech/code0-identities
|
|
151
181
|
changelog_uri: https://github.com/code0-tech/code0-identities/releases
|
|
152
182
|
rubygems_mfa_required: 'true'
|
|
153
|
-
post_install_message:
|
|
154
183
|
rdoc_options: []
|
|
155
184
|
require_paths:
|
|
156
185
|
- lib
|
|
@@ -158,15 +187,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
158
187
|
requirements:
|
|
159
188
|
- - ">="
|
|
160
189
|
- !ruby/object:Gem::Version
|
|
161
|
-
version: 3.
|
|
190
|
+
version: 3.2.0
|
|
162
191
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
163
192
|
requirements:
|
|
164
193
|
- - ">="
|
|
165
194
|
- !ruby/object:Gem::Version
|
|
166
195
|
version: '0'
|
|
167
196
|
requirements: []
|
|
168
|
-
rubygems_version: 3.
|
|
169
|
-
signing_key:
|
|
197
|
+
rubygems_version: 3.6.9
|
|
170
198
|
specification_version: 4
|
|
171
199
|
summary: Library to manage external identities
|
|
172
200
|
test_files: []
|