osso 0.0.3.14 → 0.0.3.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.buildkite/pipeline.yml +17 -1
- data/.rubocop.yml +1 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +10 -2
- data/README.md +3 -2
- data/bin/publish +18 -0
- data/db/schema.rb +9 -1
- data/lib/osso/db/migrate/20200826201852_create_app_config.rb +11 -0
- data/lib/osso/graphql/mutation.rb +7 -0
- data/lib/osso/graphql/mutations.rb +2 -0
- data/lib/osso/graphql/mutations/base_mutation.rb +18 -5
- data/lib/osso/graphql/mutations/configure_identity_provider.rb +8 -10
- data/lib/osso/graphql/mutations/create_enterprise_account.rb +7 -0
- data/lib/osso/graphql/mutations/create_identity_provider.rb +14 -5
- data/lib/osso/graphql/mutations/create_oauth_client.rb +1 -3
- data/lib/osso/graphql/mutations/delete_enterprise_account.rb +9 -11
- data/lib/osso/graphql/mutations/delete_oauth_client.rb +1 -3
- data/lib/osso/graphql/mutations/regenerate_oauth_credentials.rb +1 -3
- data/lib/osso/graphql/mutations/set_redirect_uris.rb +2 -4
- data/lib/osso/graphql/mutations/update_app_config.rb +30 -0
- data/lib/osso/graphql/query.rb +14 -0
- data/lib/osso/graphql/resolvers.rb +1 -0
- data/lib/osso/graphql/resolvers/base_resolver.rb +21 -0
- data/lib/osso/graphql/resolvers/enterprise_account.rb +1 -11
- data/lib/osso/graphql/resolvers/enterprise_accounts.rb +2 -2
- data/lib/osso/graphql/resolvers/oauth_clients.rb +2 -2
- data/lib/osso/graphql/types.rb +2 -1
- data/lib/osso/graphql/types/admin_user.rb +22 -0
- data/lib/osso/graphql/types/app_config.rb +22 -0
- data/lib/osso/graphql/types/base_object.rb +22 -0
- data/lib/osso/graphql/types/enterprise_account.rb +0 -5
- data/lib/osso/graphql/types/identity_provider.rb +0 -6
- data/lib/osso/graphql/types/oauth_client.rb +2 -4
- data/lib/osso/graphql/types/redirect_uri.rb +2 -4
- data/lib/osso/helpers/auth.rb +40 -18
- data/lib/osso/lib/route_map.rb +2 -2
- data/lib/osso/models/app_config.rb +33 -0
- data/lib/osso/models/identity_provider.rb +6 -12
- data/lib/osso/models/models.rb +1 -0
- data/lib/osso/models/oauth_client.rb +1 -0
- data/lib/osso/models/redirect_uri.rb +0 -11
- data/lib/osso/routes/admin.rb +2 -2
- data/lib/osso/routes/auth.rb +29 -12
- data/lib/osso/routes/oauth.rb +25 -18
- data/lib/osso/version.rb +1 -1
- data/lib/tasks/bootstrap.rake +2 -0
- data/spec/graphql/mutations/configure_identity_provider_spec.rb +17 -4
- data/spec/graphql/mutations/create_enterprise_account_spec.rb +53 -4
- data/spec/graphql/mutations/create_identity_provider_spec.rb +18 -6
- data/spec/graphql/mutations/create_oauth_client_spec.rb +10 -3
- data/spec/graphql/mutations/delete_enterprise_account_spec.rb +18 -4
- data/spec/graphql/mutations/delete_oauth_client_spec.rb +8 -4
- data/spec/graphql/query/enterprise_account_spec.rb +21 -6
- data/spec/graphql/query/enterprise_accounts_spec.rb +4 -2
- data/spec/graphql/query/identity_provider_spec.rb +16 -6
- data/spec/graphql/query/oauth_clients_spec.rb +10 -7
- data/spec/helpers/auth_spec.rb +97 -0
- data/spec/models/identity_provider_spec.rb +12 -0
- data/spec/routes/auth_spec.rb +18 -0
- data/spec/routes/oauth_spec.rb +5 -2
- data/spec/spec_helper.rb +3 -0
- data/spec/support/views/error.erb +0 -0
- metadata +15 -6
- data/lib/osso/graphql/types/user.rb +0 -17
@@ -29,21 +29,25 @@ describe Osso::GraphQL::Schema do
|
|
29
29
|
described_class.execute(
|
30
30
|
mutation,
|
31
31
|
variables: variables,
|
32
|
-
context:
|
32
|
+
context: current_context,
|
33
33
|
)
|
34
34
|
end
|
35
35
|
|
36
36
|
describe 'for an admin user' do
|
37
|
-
let(:
|
37
|
+
let(:current_context) do
|
38
|
+
{ scope: 'admin' }
|
39
|
+
end
|
38
40
|
it 'deletes the OauthClient' do
|
39
41
|
expect { subject }.to change { Osso::Models::OauthClient.count }.by(-1)
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
43
45
|
describe 'for an email scoped user' do
|
44
|
-
let(:
|
46
|
+
let(:current_context) do
|
47
|
+
{ scope: 'end-user', email: 'user@foo.com' }
|
48
|
+
end
|
45
49
|
|
46
|
-
it 'does not
|
50
|
+
it 'does not deletes the OauthClient' do
|
47
51
|
expect { subject }.to_not(change { Osso::Models::OauthClient.count })
|
48
52
|
end
|
49
53
|
end
|
@@ -37,12 +37,17 @@ describe Osso::GraphQL::Schema do
|
|
37
37
|
described_class.execute(
|
38
38
|
query,
|
39
39
|
variables: variables,
|
40
|
-
context:
|
40
|
+
context: current_context,
|
41
41
|
)
|
42
42
|
end
|
43
43
|
|
44
44
|
describe 'for an admin user' do
|
45
|
-
let(:
|
45
|
+
let(:current_context) do
|
46
|
+
{
|
47
|
+
scope: 'admin',
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
46
51
|
it 'returns Enterprise Account for domain' do
|
47
52
|
expect(subject['errors']).to be_nil
|
48
53
|
expect(subject.dig('data', 'enterpriseAccount', 'domain')).to eq(domain)
|
@@ -50,7 +55,12 @@ describe Osso::GraphQL::Schema do
|
|
50
55
|
end
|
51
56
|
|
52
57
|
describe 'for an email scoped user' do
|
53
|
-
let(:
|
58
|
+
let(:current_context) do
|
59
|
+
{
|
60
|
+
scope: 'end-user',
|
61
|
+
email: "user@#{domain}",
|
62
|
+
}
|
63
|
+
end
|
54
64
|
it 'returns Enterprise Account for domain' do
|
55
65
|
expect(subject['errors']).to be_nil
|
56
66
|
expect(subject.dig('data', 'enterpriseAccount', 'domain')).to eq(domain)
|
@@ -58,9 +68,14 @@ describe Osso::GraphQL::Schema do
|
|
58
68
|
end
|
59
69
|
|
60
70
|
describe 'for the wrong email scoped user' do
|
61
|
-
let(:
|
62
|
-
|
63
|
-
|
71
|
+
let(:current_context) do
|
72
|
+
{
|
73
|
+
scope: 'end-user',
|
74
|
+
email: 'foo@bar.com',
|
75
|
+
}
|
76
|
+
end
|
77
|
+
it 'does not return Enterprise Account for domain' do
|
78
|
+
expect(subject['errors']).to_not be_nil
|
64
79
|
expect(subject.dig('data', 'enterpriseAccount')).to be_nil
|
65
80
|
end
|
66
81
|
end
|
@@ -5,7 +5,9 @@ require 'spec_helper'
|
|
5
5
|
describe Osso::GraphQL::Schema do
|
6
6
|
describe 'EnterpriseAccounts' do
|
7
7
|
describe 'for an admin user' do
|
8
|
-
let(:
|
8
|
+
let(:current_context) do
|
9
|
+
{ scope: 'admin' }
|
10
|
+
end
|
9
11
|
|
10
12
|
it 'returns paginated Enterprise Accounts' do
|
11
13
|
%w[A B C].map do |name|
|
@@ -44,7 +46,7 @@ describe Osso::GraphQL::Schema do
|
|
44
46
|
response = described_class.execute(
|
45
47
|
query,
|
46
48
|
variables: { first: 2, sortOrder: 'descending', sortColumn: 'name' },
|
47
|
-
context:
|
49
|
+
context: current_context,
|
48
50
|
)
|
49
51
|
|
50
52
|
expect(response['errors']).to be_nil
|
@@ -32,12 +32,14 @@ describe Osso::GraphQL::Schema do
|
|
32
32
|
described_class.execute(
|
33
33
|
query,
|
34
34
|
variables: variables,
|
35
|
-
context:
|
35
|
+
context: current_context,
|
36
36
|
)
|
37
37
|
end
|
38
38
|
|
39
39
|
describe 'for an admin user' do
|
40
|
-
let(:
|
40
|
+
let(:current_context) do
|
41
|
+
{ scope: 'admin' }
|
42
|
+
end
|
41
43
|
it 'returns Identity Provider for id' do
|
42
44
|
expect(subject['errors']).to be_nil
|
43
45
|
expect(subject.dig('data', 'identityProvider', 'id')).to eq(id)
|
@@ -45,8 +47,12 @@ describe Osso::GraphQL::Schema do
|
|
45
47
|
end
|
46
48
|
|
47
49
|
describe 'for an email scoped user' do
|
48
|
-
let(:
|
49
|
-
|
50
|
+
let(:current_context) do
|
51
|
+
{
|
52
|
+
scope: 'end-user',
|
53
|
+
email: "user@#{domain}",
|
54
|
+
}
|
55
|
+
end
|
50
56
|
it 'returns Enterprise Account for domain' do
|
51
57
|
expect(subject['errors']).to be_nil
|
52
58
|
expect(subject.dig('data', 'identityProvider', 'domain')).to eq(domain)
|
@@ -54,8 +60,12 @@ describe Osso::GraphQL::Schema do
|
|
54
60
|
end
|
55
61
|
|
56
62
|
describe 'for the wrong email scoped user' do
|
57
|
-
let(:
|
58
|
-
|
63
|
+
let(:current_context) do
|
64
|
+
{
|
65
|
+
scope: 'end-user',
|
66
|
+
email: 'user@bar.com',
|
67
|
+
}
|
68
|
+
end
|
59
69
|
it 'returns Enterprise Account for domain' do
|
60
70
|
expect(subject['errors']).to_not be_empty
|
61
71
|
expect(subject.dig('data', 'enterpriseAccount')).to be_nil
|
@@ -25,12 +25,14 @@ describe Osso::GraphQL::Schema do
|
|
25
25
|
described_class.execute(
|
26
26
|
query,
|
27
27
|
variables: nil,
|
28
|
-
context:
|
28
|
+
context: current_context,
|
29
29
|
)
|
30
30
|
end
|
31
31
|
|
32
32
|
describe 'for an admin user' do
|
33
|
-
let(:
|
33
|
+
let(:current_context) do
|
34
|
+
{ scope: 'admin' }
|
35
|
+
end
|
34
36
|
|
35
37
|
it 'returns Oauth Clients' do
|
36
38
|
expect(subject['errors']).to be_nil
|
@@ -38,11 +40,12 @@ describe Osso::GraphQL::Schema do
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
describe 'for an
|
42
|
-
let(:
|
43
|
-
|
44
|
-
|
45
|
-
|
43
|
+
describe 'for an internal scoped user' do
|
44
|
+
let(:current_context) do
|
45
|
+
{ scope: 'internal' }
|
46
|
+
end
|
47
|
+
it 'does not return Oauth Clients' do
|
48
|
+
expect(subject['errors']).to_not be_nil
|
46
49
|
expect(subject.dig('data', 'oauthClients')).to be_nil
|
47
50
|
end
|
48
51
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Osso::Helpers::Auth do
|
6
|
+
before do
|
7
|
+
ENV['JWT_HMAC_SECRET'] = 'super-secret'
|
8
|
+
end
|
9
|
+
|
10
|
+
subject(:app) do
|
11
|
+
Class.new { include Osso::Helpers::Auth }
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'with the token as a header' do
|
15
|
+
before do
|
16
|
+
allow_any_instance_of(subject).to receive(:request) do
|
17
|
+
double('Request', env: { 'admin_token' => token }, post?: false)
|
18
|
+
end
|
19
|
+
|
20
|
+
allow_any_instance_of(subject).to receive(:redirect) do
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'with an admin token' do
|
26
|
+
let(:token) { encode({ scope: 'admin' }) }
|
27
|
+
|
28
|
+
it 'allows #token_protected! methods' do
|
29
|
+
expect(subject.new.token_protected!).to_not be(false)
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'allows #enterprise_protected! methods' do
|
33
|
+
expect(subject.new.enterprise_protected!).to_not be(false)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'allows #internal_protected! methods' do
|
37
|
+
expect(subject.new.internal_protected!).to_not be(false)
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'allows #admin_protected! methods' do
|
41
|
+
expect(subject.new.admin_protected!).to_not be(false)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'with an internal token' do
|
46
|
+
let(:token) { encode({ scope: 'internal' }) }
|
47
|
+
|
48
|
+
it 'allows #token_protected! methods' do
|
49
|
+
expect(subject.new.token_protected!).to_not be(false)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'allows #enterprise_protected! methods' do
|
53
|
+
expect(subject.new.enterprise_protected!).to_not be(false)
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'allows #internal_protected! methods' do
|
57
|
+
expect(subject.new.internal_protected!).to_not be(false)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'allows #admin_protected! methods' do
|
61
|
+
expect(subject.new.admin_protected!).to be(false)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'with an end-user token' do
|
66
|
+
let(:token) { encode({ scope: 'end-user', email: 'user@example.com' }) }
|
67
|
+
|
68
|
+
it 'allows #token_protected! methods' do
|
69
|
+
expect(subject.new.token_protected!).to_not be(false)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'allows #enterprise_protected! methods for the scoped domain' do
|
73
|
+
expect(subject.new.enterprise_protected!('example.com')).to_not be(false)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'halts #enterprise_protected! methods for the wrong scoped domain' do
|
77
|
+
expect(subject.new.enterprise_protected!('foo.com')).to be(false)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'halts #internal_protected! methods' do
|
81
|
+
expect(subject.new.internal_protected!).to be(false)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'halts #admin_protected! methods' do
|
85
|
+
expect(subject.new.admin_protected!).to be(false)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def encode(payload)
|
91
|
+
JWT.encode(
|
92
|
+
payload,
|
93
|
+
ENV['JWT_HMAC_SECRET'],
|
94
|
+
'HS256',
|
95
|
+
)
|
96
|
+
end
|
97
|
+
end
|
@@ -14,4 +14,16 @@ describe Osso::Models::IdentityProvider do
|
|
14
14
|
)
|
15
15
|
end
|
16
16
|
end
|
17
|
+
|
18
|
+
describe '#saml_options' do
|
19
|
+
it 'returns the required args' do
|
20
|
+
expect(subject.saml_options).
|
21
|
+
to match(
|
22
|
+
domain: subject.domain,
|
23
|
+
idp_cert: subject.sso_cert,
|
24
|
+
idp_sso_target_url: subject.sso_url,
|
25
|
+
issuer: subject.domain,
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
17
29
|
end
|
data/spec/routes/auth_spec.rb
CHANGED
@@ -63,6 +63,24 @@ describe Osso::Auth do
|
|
63
63
|
)
|
64
64
|
end.to change { Osso::Models::AuthorizationCode.count }.by(1)
|
65
65
|
end
|
66
|
+
|
67
|
+
describe 'for an IDP initiated login' do
|
68
|
+
it 'redirects with a default state' do
|
69
|
+
mock_saml_omniauth
|
70
|
+
|
71
|
+
post(
|
72
|
+
"/auth/saml/#{okta_provider.id}/callback",
|
73
|
+
nil,
|
74
|
+
{
|
75
|
+
'omniauth.auth' => OmniAuth.config.mock_auth[:saml],
|
76
|
+
'identity_provider' => okta_provider,
|
77
|
+
},
|
78
|
+
)
|
79
|
+
expect(last_response).to be_redirect
|
80
|
+
follow_redirect!
|
81
|
+
expect(last_request.url).to match(/.*state=IDP_INITIATED$/)
|
82
|
+
end
|
83
|
+
end
|
66
84
|
end
|
67
85
|
|
68
86
|
describe 'on subsequent authentications' do
|
data/spec/routes/oauth_spec.rb
CHANGED
@@ -8,7 +8,10 @@ describe Osso::Oauth do
|
|
8
8
|
describe 'get /oauth/authorize' do
|
9
9
|
describe 'with a valid client ID and redirect URI' do
|
10
10
|
describe 'for a domain that does not belong to an enterprise' do
|
11
|
-
|
11
|
+
# TODO: better error handling and test
|
12
|
+
it 'renders an error page' do
|
13
|
+
described_class.set(:views, spec_views)
|
14
|
+
|
12
15
|
create(:enterprise_with_okta, domain: 'foo.com')
|
13
16
|
|
14
17
|
get(
|
@@ -19,7 +22,7 @@ describe Osso::Oauth do
|
|
19
22
|
redirect_uri: client.redirect_uri_values.sample,
|
20
23
|
)
|
21
24
|
|
22
|
-
expect(last_response.status).to eq(
|
25
|
+
expect(last_response.status).to eq(200)
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
data/spec/spec_helper.rb
CHANGED
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: osso
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.3.
|
4
|
+
version: 0.0.3.19
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Bauch
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -226,6 +226,7 @@ email:
|
|
226
226
|
executables:
|
227
227
|
- annotate
|
228
228
|
- console
|
229
|
+
- publish
|
229
230
|
- setup
|
230
231
|
extensions: []
|
231
232
|
extra_rdoc_files: []
|
@@ -246,6 +247,7 @@ files:
|
|
246
247
|
- Rakefile
|
247
248
|
- bin/annotate
|
248
249
|
- bin/console
|
250
|
+
- bin/publish
|
249
251
|
- bin/setup
|
250
252
|
- config/database.yml
|
251
253
|
- db/schema.rb
|
@@ -268,6 +270,7 @@ files:
|
|
268
270
|
- lib/osso/db/migrate/20200722230116_add_identity_provider_status_enum_and_use_on_identity_providers.rb
|
269
271
|
- lib/osso/db/migrate/20200723153750_add_missing_timestamps.rb
|
270
272
|
- lib/osso/db/migrate/20200723162228_drop_unneeded_tables.rb
|
273
|
+
- lib/osso/db/migrate/20200826201852_create_app_config.rb
|
271
274
|
- lib/osso/graphql/.DS_Store
|
272
275
|
- lib/osso/graphql/mutation.rb
|
273
276
|
- lib/osso/graphql/mutations.rb
|
@@ -280,13 +283,17 @@ files:
|
|
280
283
|
- lib/osso/graphql/mutations/delete_oauth_client.rb
|
281
284
|
- lib/osso/graphql/mutations/regenerate_oauth_credentials.rb
|
282
285
|
- lib/osso/graphql/mutations/set_redirect_uris.rb
|
286
|
+
- lib/osso/graphql/mutations/update_app_config.rb
|
283
287
|
- lib/osso/graphql/query.rb
|
284
288
|
- lib/osso/graphql/resolvers.rb
|
289
|
+
- lib/osso/graphql/resolvers/base_resolver.rb
|
285
290
|
- lib/osso/graphql/resolvers/enterprise_account.rb
|
286
291
|
- lib/osso/graphql/resolvers/enterprise_accounts.rb
|
287
292
|
- lib/osso/graphql/resolvers/oauth_clients.rb
|
288
293
|
- lib/osso/graphql/schema.rb
|
289
294
|
- lib/osso/graphql/types.rb
|
295
|
+
- lib/osso/graphql/types/admin_user.rb
|
296
|
+
- lib/osso/graphql/types/app_config.rb
|
290
297
|
- lib/osso/graphql/types/base_connection.rb
|
291
298
|
- lib/osso/graphql/types/base_enum.rb
|
292
299
|
- lib/osso/graphql/types/base_input_object.rb
|
@@ -298,13 +305,13 @@ files:
|
|
298
305
|
- lib/osso/graphql/types/oauth_client.rb
|
299
306
|
- lib/osso/graphql/types/redirect_uri.rb
|
300
307
|
- lib/osso/graphql/types/redirect_uri_input.rb
|
301
|
-
- lib/osso/graphql/types/user.rb
|
302
308
|
- lib/osso/helpers/auth.rb
|
303
309
|
- lib/osso/helpers/helpers.rb
|
304
310
|
- lib/osso/lib/app_config.rb
|
305
311
|
- lib/osso/lib/oauth2_token.rb
|
306
312
|
- lib/osso/lib/route_map.rb
|
307
313
|
- lib/osso/models/access_token.rb
|
314
|
+
- lib/osso/models/app_config.rb
|
308
315
|
- lib/osso/models/authorization_code.rb
|
309
316
|
- lib/osso/models/enterprise_account.rb
|
310
317
|
- lib/osso/models/identity_provider.rb
|
@@ -338,6 +345,7 @@ files:
|
|
338
345
|
- spec/graphql/query/enterprise_accounts_spec.rb
|
339
346
|
- spec/graphql/query/identity_provider_spec.rb
|
340
347
|
- spec/graphql/query/oauth_clients_spec.rb
|
348
|
+
- spec/helpers/auth_spec.rb
|
341
349
|
- spec/models/azure_saml_provider_spec.rb
|
342
350
|
- spec/models/identity_provider_spec.rb
|
343
351
|
- spec/models/okta_saml_provider_spec.rb
|
@@ -348,11 +356,12 @@ files:
|
|
348
356
|
- spec/spec_helper.rb
|
349
357
|
- spec/support/spec_app.rb
|
350
358
|
- spec/support/views/admin.erb
|
359
|
+
- spec/support/views/error.erb
|
351
360
|
homepage: https://github.com/enterprise-oss/osso-rb
|
352
361
|
licenses:
|
353
362
|
- MIT
|
354
363
|
metadata: {}
|
355
|
-
post_install_message:
|
364
|
+
post_install_message:
|
356
365
|
rdoc_options: []
|
357
366
|
require_paths:
|
358
367
|
- lib
|
@@ -368,7 +377,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
368
377
|
version: '0'
|
369
378
|
requirements: []
|
370
379
|
rubygems_version: 3.0.3
|
371
|
-
signing_key:
|
380
|
+
signing_key:
|
372
381
|
specification_version: 4
|
373
382
|
summary: Main functionality for Osso
|
374
383
|
test_files: []
|