osso 0.0.8 → 0.1.2
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 +1 -0
- data/.rubocop.yml +4 -16
- data/Gemfile +2 -2
- data/Gemfile.lock +60 -55
- data/Rakefile +1 -0
- data/bin/console +3 -0
- data/db/schema.rb +4 -4
- data/lib/osso.rb +1 -0
- data/lib/osso/db/migrate/20210201220556_add_generic_saml_to_provider_service_enum.rb +28 -0
- data/lib/osso/graphql/mutations/configure_identity_provider.rb +4 -1
- data/lib/osso/graphql/mutations/create_enterprise_account.rb +4 -1
- data/lib/osso/graphql/mutations/create_identity_provider.rb +8 -3
- data/lib/osso/graphql/mutations/create_oauth_client.rb +4 -1
- data/lib/osso/graphql/mutations/delete_enterprise_account.rb +4 -1
- data/lib/osso/graphql/mutations/delete_identity_provider.rb +4 -1
- data/lib/osso/graphql/mutations/delete_oauth_client.rb +4 -1
- data/lib/osso/graphql/mutations/invite_admin_user.rb +6 -0
- data/lib/osso/graphql/mutations/regenerate_oauth_credentials.rb +10 -1
- data/lib/osso/graphql/mutations/set_redirect_uris.rb +2 -0
- data/lib/osso/graphql/mutations/update_app_config.rb +4 -1
- data/lib/osso/graphql/query.rb +26 -31
- data/lib/osso/graphql/schema.rb +0 -1
- data/lib/osso/graphql/types/identity_provider_service.rb +1 -0
- data/lib/osso/lib/analytics.rb +55 -0
- data/lib/osso/lib/route_map.rb +2 -0
- data/lib/osso/models/account.rb +1 -1
- data/lib/osso/models/identity_provider.rb +3 -3
- data/lib/osso/routes/admin.rb +47 -5
- data/lib/osso/routes/auth.rb +2 -0
- data/lib/osso/routes/oauth.rb +1 -1
- data/lib/osso/version.rb +1 -1
- data/lib/tasks/bootstrap.rake +6 -4
- data/osso-rb.gemspec +5 -3
- data/spec/graphql/mutations/create_identity_provider_spec.rb +1 -1
- data/spec/models/identity_provider_spec.rb +1 -1
- data/spec/routes/admin_spec.rb +54 -9
- data/spec/routes/auth_spec.rb +5 -3
- data/spec/routes/oauth_spec.rb +7 -13
- data/spec/spec_helper.rb +2 -0
- data/spec/support/views/saml_login_form.erb +1 -0
- metadata +39 -15
- data/spec/routes/app_spec.rb +0 -6
@@ -18,7 +18,10 @@ module Osso
|
|
18
18
|
def resolve(**args)
|
19
19
|
customer = enterprise_account(**args)
|
20
20
|
|
21
|
-
|
21
|
+
if customer.destroy
|
22
|
+
Osso::Analytics.capture(email: context[:email], event: self.class.name.demodulize, properties: args)
|
23
|
+
return response_data(enterprise_account: nil)
|
24
|
+
end
|
22
25
|
|
23
26
|
response_error(customer.errors)
|
24
27
|
end
|
@@ -14,7 +14,10 @@ module Osso
|
|
14
14
|
def resolve(id:)
|
15
15
|
identity_provider = Osso::Models::IdentityProvider.find(id)
|
16
16
|
|
17
|
-
|
17
|
+
if identity_provider.destroy
|
18
|
+
Osso::Analytics.capture(email: context[:email], event: self.class.name.demodulize, properties: { id: id })
|
19
|
+
return response_data(identity_provider: nil)
|
20
|
+
end
|
18
21
|
|
19
22
|
response_error(identity_provider.errors)
|
20
23
|
end
|
@@ -14,7 +14,10 @@ module Osso
|
|
14
14
|
def resolve(id:)
|
15
15
|
oauth_client = Osso::Models::OauthClient.find(id)
|
16
16
|
|
17
|
-
|
17
|
+
if oauth_client.destroy
|
18
|
+
Osso::Analytics.capture(email: context[:email], event: self.class.name.demodulize, properties: { id: id })
|
19
|
+
return response_data(oauth_client: nil)
|
20
|
+
end
|
18
21
|
|
19
22
|
response_error(oauth_client.errors)
|
20
23
|
end
|
@@ -23,6 +23,12 @@ module Osso
|
|
23
23
|
if admin_user.save
|
24
24
|
verify_user(email)
|
25
25
|
|
26
|
+
Osso::Analytics.capture(email: context[:email], event: self.class.name.demodulize, properties: {
|
27
|
+
invited_email: email,
|
28
|
+
invited_role: role,
|
29
|
+
invited_oauth_client_id: oauth_client_id,
|
30
|
+
})
|
31
|
+
|
26
32
|
return response_data(admin_user: admin_user)
|
27
33
|
end
|
28
34
|
|
@@ -15,7 +15,16 @@ module Osso
|
|
15
15
|
oauth_client = Osso::Models::OauthClient.find(id)
|
16
16
|
oauth_client.regenerate_secrets!
|
17
17
|
|
18
|
-
|
18
|
+
if oauth_client.save
|
19
|
+
Osso::Analytics.capture(
|
20
|
+
email: context[:email],
|
21
|
+
event: self.class.name.demodulize,
|
22
|
+
properties: {
|
23
|
+
oauth_client_id: id
|
24
|
+
}
|
25
|
+
)
|
26
|
+
return response_data(oauth_client: oauth_client)
|
27
|
+
end
|
19
28
|
|
20
29
|
response_error(oauth_client.errors)
|
21
30
|
end
|
@@ -18,6 +18,8 @@ module Osso
|
|
18
18
|
update_existing(oauth_client, redirect_uris)
|
19
19
|
create_new(oauth_client, redirect_uris)
|
20
20
|
|
21
|
+
Osso::Analytics.capture(email: context[:email], event: self.class.name.demodulize, properties: redirect_uris)
|
22
|
+
|
21
23
|
response_data(oauth_client: oauth_client.reload)
|
22
24
|
rescue StandardError => e
|
23
25
|
response_error(e)
|
@@ -15,7 +15,10 @@ module Osso
|
|
15
15
|
|
16
16
|
def resolve(**args)
|
17
17
|
app_config = Osso::Models::AppConfig.find
|
18
|
-
|
18
|
+
if app_config.update(**args)
|
19
|
+
Osso::Analytics.capture(email: context[:email], event: self.class.name.demodulize, properties: args)
|
20
|
+
return response_data(app_config: app_config)
|
21
|
+
end
|
19
22
|
|
20
23
|
response_error(app_config.errors)
|
21
24
|
end
|
data/lib/osso/graphql/query.rb
CHANGED
@@ -16,44 +16,39 @@ module Osso
|
|
16
16
|
|
17
17
|
field :oauth_clients, null: true, resolver: Resolvers::OAuthClients
|
18
18
|
|
19
|
-
field
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
19
|
+
field :admin_users, [Types::AdminUser], null: false
|
20
|
+
|
21
|
+
field :app_config, Types::AppConfig, null: false
|
22
|
+
|
23
|
+
field :current_user, Types::AdminUser, null: false
|
24
|
+
|
25
|
+
field :identity_provider, Types::IdentityProvider, null: true do
|
25
26
|
argument :id, ID, required: true
|
26
27
|
end
|
27
28
|
|
28
|
-
field
|
29
|
-
:app_config,
|
30
|
-
Types::AppConfig,
|
31
|
-
null: false,
|
32
|
-
resolve: ->(_obj, _args, _context) { Osso::Models::AppConfig.find },
|
33
|
-
)
|
34
|
-
|
35
|
-
field(
|
36
|
-
:oauth_client,
|
37
|
-
Types::OauthClient,
|
38
|
-
null: true,
|
39
|
-
resolve: ->(_obj, args, _context) { Osso::Models::OauthClient.find(args[:id]) },
|
40
|
-
) do
|
29
|
+
field :oauth_client, Types::OauthClient, null: true do
|
41
30
|
argument :id, ID, required: true
|
42
31
|
end
|
43
32
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
33
|
+
def admin_users
|
34
|
+
Osso::Models::Account.all
|
35
|
+
end
|
36
|
+
|
37
|
+
def app_config
|
38
|
+
Osso::Models::AppConfig.find
|
39
|
+
end
|
40
|
+
|
41
|
+
def current_user
|
42
|
+
context.to_h
|
43
|
+
end
|
50
44
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
45
|
+
def identity_provider(id:)
|
46
|
+
Osso::Models::IdentityProvider.find(id)
|
47
|
+
end
|
48
|
+
|
49
|
+
def oauth_client(id:)
|
50
|
+
Osso::Models::OauthClient.find(id)
|
51
|
+
end
|
57
52
|
end
|
58
53
|
end
|
59
54
|
end
|
data/lib/osso/graphql/schema.rb
CHANGED
@@ -8,6 +8,7 @@ module Osso
|
|
8
8
|
value('GOOGLE', 'Google SAML Identity Provider', value: 'GOOGLE')
|
9
9
|
value('OKTA', 'Okta Identity Provider', value: 'OKTA')
|
10
10
|
value('ONELOGIN', 'OneLogin Identity Provider', value: 'ONELOGIN')
|
11
|
+
value('OTHER', 'Generic SAML Identity Provider', value: 'OTHER')
|
11
12
|
value('PING', 'PingID Identity Provider', value: 'PING')
|
12
13
|
value('SALESFORCE', 'Salesforce Identity Provider', value: 'SALESFORCE')
|
13
14
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'posthog-ruby'
|
4
|
+
|
5
|
+
module Osso
|
6
|
+
# Osso::Analytics provides an interface to track product analytics for any provider.
|
7
|
+
# Osso recommends PostHog as an open source solution for your product analytics needs.
|
8
|
+
# If you want to use another product analytics provider, you can patch the Osso::Analytics
|
9
|
+
# class yourself in your parent application. Be sure to implement the public
|
10
|
+
# .identify and .capture class methods with the required method signatures and require
|
11
|
+
# your class after requiring Osso.
|
12
|
+
class Analytics
|
13
|
+
class << self
|
14
|
+
def identify(email:, properties: {})
|
15
|
+
return unless configured?
|
16
|
+
|
17
|
+
client.identify({
|
18
|
+
distinct_id: email,
|
19
|
+
properties: properties.merge(instance_properties),
|
20
|
+
})
|
21
|
+
end
|
22
|
+
|
23
|
+
def capture(email:, event:, properties: {})
|
24
|
+
return unless configured?
|
25
|
+
|
26
|
+
client.capture(
|
27
|
+
distinct_id: email,
|
28
|
+
event: event,
|
29
|
+
properties: properties.merge(instance_properties),
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def configured?
|
36
|
+
ENV['POSTHOG_API_KEY'].present?
|
37
|
+
end
|
38
|
+
|
39
|
+
def client
|
40
|
+
@client ||= PostHog::Client.new({
|
41
|
+
api_key: ENV['POSTHOG_API_KEY'],
|
42
|
+
api_host: ENV['POSTHOG_HOST'],
|
43
|
+
on_error: proc { |_status, msg| print msg },
|
44
|
+
})
|
45
|
+
end
|
46
|
+
|
47
|
+
def instance_properties
|
48
|
+
{
|
49
|
+
instance_url: ENV['BASE_URL'],
|
50
|
+
osso_plan: ENV['OSSO_PLAN'],
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/osso/lib/route_map.rb
CHANGED
data/lib/osso/models/account.rb
CHANGED
@@ -18,7 +18,7 @@ module Osso
|
|
18
18
|
|
19
19
|
ENTITY_ID_URI_REQUIRED = [
|
20
20
|
'PING',
|
21
|
-
]
|
21
|
+
].freeze
|
22
22
|
|
23
23
|
def name
|
24
24
|
service.titlecase
|
@@ -30,7 +30,7 @@ module Osso
|
|
30
30
|
idp_sso_target_url: sso_url,
|
31
31
|
idp_cert: sso_cert,
|
32
32
|
issuer: sso_issuer,
|
33
|
-
name_identifier_format:
|
33
|
+
name_identifier_format: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
34
34
|
}
|
35
35
|
end
|
36
36
|
|
@@ -56,7 +56,7 @@ module Osso
|
|
56
56
|
|
57
57
|
def set_sso_issuer
|
58
58
|
parts = [domain, oauth_client_id]
|
59
|
-
|
59
|
+
|
60
60
|
parts.unshift('https:/') if ENTITY_ID_URI_REQUIRED.any?(service)
|
61
61
|
|
62
62
|
self.sso_issuer = parts.join('/')
|
data/lib/osso/routes/admin.rb
CHANGED
@@ -10,16 +10,46 @@ module Osso
|
|
10
10
|
DB = Sequel.postgres(extensions: :activerecord_connection)
|
11
11
|
use Rack::Session::Cookie, secret: ENV.fetch('SESSION_SECRET')
|
12
12
|
|
13
|
+
plugin :json
|
14
|
+
plugin :json_parser
|
13
15
|
plugin :middleware
|
14
16
|
plugin :render, engine: 'erb', views: ENV['RODAUTH_VIEWS'] || DEFAULT_VIEWS_DIR
|
15
17
|
plugin :route_csrf
|
16
18
|
|
17
19
|
plugin :rodauth do
|
18
|
-
enable :login, :verify_account
|
20
|
+
enable :login, :verify_account, :jwt
|
21
|
+
|
22
|
+
base_uri = URI.parse(ENV.fetch('BASE_URL'))
|
23
|
+
base_url base_uri
|
24
|
+
domain base_uri.host
|
25
|
+
|
26
|
+
jwt_secret ENV.fetch('SESSION_SECRET')
|
27
|
+
only_json? false
|
28
|
+
|
29
|
+
email_from { "Osso <no-reply@#{domain}>" }
|
19
30
|
verify_account_set_password? true
|
20
|
-
already_logged_in { redirect login_redirect }
|
21
31
|
use_database_authentication_functions? false
|
22
32
|
|
33
|
+
after_login do
|
34
|
+
Osso::Analytics.identify(email: account[:email], properties: account)
|
35
|
+
end
|
36
|
+
|
37
|
+
verify_account_view do
|
38
|
+
render :admin
|
39
|
+
end
|
40
|
+
|
41
|
+
login_view do
|
42
|
+
render :admin
|
43
|
+
end
|
44
|
+
|
45
|
+
verify_account_email_subject do
|
46
|
+
DB[:accounts].one? ? 'Your Osso instance is ready' : 'You\'ve been invited to start using Osso'
|
47
|
+
end
|
48
|
+
|
49
|
+
verify_account_email_body do
|
50
|
+
DB[:accounts].one? ? render('verify-first-account-email') : render('verify-account-email')
|
51
|
+
end
|
52
|
+
|
23
53
|
before_create_account_route do
|
24
54
|
request.halt unless DB[:accounts].empty?
|
25
55
|
end
|
@@ -31,16 +61,28 @@ module Osso
|
|
31
61
|
r.rodauth
|
32
62
|
|
33
63
|
def current_account
|
34
|
-
Osso::Models::Account.find(
|
35
|
-
|
64
|
+
Osso::Models::Account.find(
|
65
|
+
rodauth.
|
66
|
+
session.
|
67
|
+
to_hash.
|
68
|
+
stringify_keys['account_id'],
|
69
|
+
).context.
|
36
70
|
merge({ rodauth: rodauth })
|
37
71
|
end
|
38
72
|
|
39
73
|
r.on 'admin' do
|
40
|
-
rodauth.require_authentication
|
41
74
|
erb :admin, layout: false
|
42
75
|
end
|
43
76
|
|
77
|
+
r.post 'idp' do
|
78
|
+
onboarded = Osso::Models::IdentityProvider.
|
79
|
+
not_pending.
|
80
|
+
where(domain: r.params['domain']).
|
81
|
+
exists?
|
82
|
+
|
83
|
+
{ onboarded: onboarded }.to_json
|
84
|
+
end
|
85
|
+
|
44
86
|
r.post 'graphql' do
|
45
87
|
rodauth.require_authentication
|
46
88
|
|
data/lib/osso/routes/auth.rb
CHANGED
@@ -14,6 +14,8 @@ module Osso
|
|
14
14
|
/[0-9a-f]{8}-[0-9a-f]{3,4}-[0-9a-f]{4}-[0-9a-f]{3,4}-[0-9a-f]{12}/.
|
15
15
|
freeze
|
16
16
|
|
17
|
+
use Rack::Protection, allow_if: ->(env) { Rack::Request.new(env)&.path&.end_with?('callback') }
|
18
|
+
|
17
19
|
use OmniAuth::Builder do
|
18
20
|
OmniAuth::MultiProvider.register(
|
19
21
|
self,
|
data/lib/osso/routes/oauth.rb
CHANGED
data/lib/osso/version.rb
CHANGED
data/lib/tasks/bootstrap.rake
CHANGED
@@ -8,9 +8,11 @@ namespace :osso do
|
|
8
8
|
desc 'Bootstrap Osso data for a deployment'
|
9
9
|
task :bootstrap do
|
10
10
|
%w[Production Staging Development].each do |environment|
|
11
|
+
next if Osso::Models::OauthClient.find_by_name(environment)
|
12
|
+
|
11
13
|
Osso::Models::OauthClient.create!(
|
12
14
|
name: environment,
|
13
|
-
)
|
15
|
+
)
|
14
16
|
end
|
15
17
|
|
16
18
|
Osso::Models::AppConfig.create
|
@@ -18,7 +20,7 @@ namespace :osso do
|
|
18
20
|
admin_email = ENV['ADMIN_EMAIL']
|
19
21
|
|
20
22
|
if admin_email
|
21
|
-
|
23
|
+
Osso::Models::Account.create(
|
22
24
|
email: admin_email,
|
23
25
|
status_id: 1,
|
24
26
|
role: 'admin',
|
@@ -29,10 +31,10 @@ namespace :osso do
|
|
29
31
|
rodauth = Osso::Admin.rodauth.new(Osso::Admin.new({
|
30
32
|
'HTTP_HOST' => base_uri.host,
|
31
33
|
'SERVER_NAME' => base_uri.to_s,
|
32
|
-
'rack.url_scheme' => base_uri.scheme
|
34
|
+
'rack.url_scheme' => base_uri.scheme,
|
33
35
|
}))
|
34
36
|
|
35
|
-
|
37
|
+
rodauth.account_from_login(admin_email)
|
36
38
|
rodauth.setup_account_verification
|
37
39
|
end
|
38
40
|
end
|
data/osso-rb.gemspec
CHANGED
@@ -22,15 +22,17 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_runtime_dependency 'mail', '~> 2.7.1'
|
23
23
|
spec.add_runtime_dependency 'omniauth-multi-provider'
|
24
24
|
spec.add_runtime_dependency 'omniauth-saml'
|
25
|
+
spec.add_runtime_dependency 'posthog-ruby'
|
25
26
|
spec.add_runtime_dependency 'rack', '>= 2.1.4'
|
26
27
|
spec.add_runtime_dependency 'rack-contrib'
|
27
28
|
spec.add_runtime_dependency 'rack-oauth2'
|
29
|
+
spec.add_runtime_dependency 'rack-protection', '~> 2.1.0'
|
28
30
|
spec.add_runtime_dependency 'rake'
|
29
|
-
spec.add_runtime_dependency 'rodauth', '~> 2.
|
30
|
-
spec.add_runtime_dependency 'sequel', '
|
31
|
+
spec.add_runtime_dependency 'rodauth', '~> 2.9'
|
32
|
+
spec.add_runtime_dependency 'sequel', '~> 5.40'
|
31
33
|
spec.add_runtime_dependency 'sequel-activerecord_connection', '>= 0.3', '< 2.0'
|
32
34
|
spec.add_runtime_dependency 'sinatra'
|
33
|
-
spec.add_runtime_dependency 'sinatra-activerecord'
|
35
|
+
spec.add_runtime_dependency 'sinatra-activerecord', '>= 2.0.22'
|
34
36
|
spec.add_runtime_dependency 'sinatra-contrib'
|
35
37
|
|
36
38
|
spec.add_development_dependency 'annotate', '~> 3.1'
|