osso 0.0.6 → 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 +4 -4
- data/.buildkite/pipeline.yml +1 -0
- data/.rubocop.yml +4 -16
- data/Gemfile +3 -3
- data/Gemfile.lock +76 -70
- data/Rakefile +3 -0
- data/bin/console +3 -0
- data/db/schema.rb +2 -2
- data/lib/osso.rb +1 -0
- data/lib/osso/db/migrate/20201125143501_add_salesforce_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 -2
- data/lib/osso/routes/admin.rb +37 -5
- data/lib/osso/routes/auth.rb +2 -0
- data/lib/osso/routes/oauth.rb +10 -4
- 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 -0
- data/spec/routes/admin_spec.rb +27 -9
- data/spec/routes/auth_spec.rb +5 -3
- data/spec/routes/oauth_spec.rb +20 -12
- data/spec/spec_helper.rb +2 -0
- data/spec/support/views/hosted_login.erb +1 -0
- data/spec/support/views/saml_login_form.erb +1 -0
- metadata +40 -9
- data/spec/routes/app_spec.rb +0 -6
@@ -14,7 +14,10 @@ module Osso
|
|
14
14
|
def resolve(**args)
|
15
15
|
oauth_client = Osso::Models::OauthClient.new(args)
|
16
16
|
|
17
|
-
|
17
|
+
if oauth_client.save
|
18
|
+
Osso::Analytics.capture(email: context[:email], event: self.class.name.demodulize, properties: args)
|
19
|
+
return response_data(oauth_client: oauth_client)
|
20
|
+
end
|
18
21
|
|
19
22
|
response_error(oauth_client.errors)
|
20
23
|
end
|
@@ -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
@@ -9,6 +9,7 @@ module Osso
|
|
9
9
|
value('OKTA', 'Okta Identity Provider', value: 'OKTA')
|
10
10
|
value('ONELOGIN', 'OneLogin Identity Provider', value: 'ONELOGIN')
|
11
11
|
value('PING', 'PingID Identity Provider', value: 'PING')
|
12
|
+
value('SALESFORCE', 'Salesforce Identity Provider', value: 'SALESFORCE')
|
12
13
|
end
|
13
14
|
end
|
14
15
|
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,6 +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: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
|
33
34
|
}
|
34
35
|
end
|
35
36
|
|
@@ -55,7 +56,7 @@ module Osso
|
|
55
56
|
|
56
57
|
def set_sso_issuer
|
57
58
|
parts = [domain, oauth_client_id]
|
58
|
-
|
59
|
+
|
59
60
|
parts.unshift('https:/') if ENTITY_ID_URI_REQUIRED.any?(service)
|
60
61
|
|
61
62
|
self.sso_issuer = parts.join('/')
|
data/lib/osso/routes/admin.rb
CHANGED
@@ -10,16 +10,45 @@ 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
|
13
14
|
plugin :middleware
|
14
15
|
plugin :render, engine: 'erb', views: ENV['RODAUTH_VIEWS'] || DEFAULT_VIEWS_DIR
|
15
16
|
plugin :route_csrf
|
16
17
|
|
17
18
|
plugin :rodauth do
|
18
|
-
enable :login, :verify_account
|
19
|
+
enable :login, :verify_account, :jwt
|
20
|
+
|
21
|
+
base_uri = URI.parse(ENV.fetch('BASE_URL'))
|
22
|
+
base_url base_uri
|
23
|
+
domain base_uri.host
|
24
|
+
|
25
|
+
jwt_secret ENV.fetch('SESSION_SECRET')
|
26
|
+
only_json? false
|
27
|
+
|
28
|
+
email_from { "Osso <no-reply@#{domain}>" }
|
19
29
|
verify_account_set_password? true
|
20
|
-
already_logged_in { redirect login_redirect }
|
21
30
|
use_database_authentication_functions? false
|
22
31
|
|
32
|
+
after_login do
|
33
|
+
Osso::Analytics.identify(email: account[:email], properties: account)
|
34
|
+
end
|
35
|
+
|
36
|
+
verify_account_view do
|
37
|
+
render :admin
|
38
|
+
end
|
39
|
+
|
40
|
+
login_view do
|
41
|
+
render :admin
|
42
|
+
end
|
43
|
+
|
44
|
+
verify_account_email_subject do
|
45
|
+
DB[:accounts].one? ? 'Your Osso instance is ready' : 'You\'ve been invited to start using Osso'
|
46
|
+
end
|
47
|
+
|
48
|
+
verify_account_email_body do
|
49
|
+
DB[:accounts].one? ? render('verify-first-account-email') : render('verify-account-email')
|
50
|
+
end
|
51
|
+
|
23
52
|
before_create_account_route do
|
24
53
|
request.halt unless DB[:accounts].empty?
|
25
54
|
end
|
@@ -31,13 +60,16 @@ module Osso
|
|
31
60
|
r.rodauth
|
32
61
|
|
33
62
|
def current_account
|
34
|
-
Osso::Models::Account.find(
|
35
|
-
|
63
|
+
Osso::Models::Account.find(
|
64
|
+
rodauth.
|
65
|
+
session.
|
66
|
+
to_hash.
|
67
|
+
stringify_keys['account_id'],
|
68
|
+
).context.
|
36
69
|
merge({ rodauth: rodauth })
|
37
70
|
end
|
38
71
|
|
39
72
|
r.on 'admin' do
|
40
|
-
rodauth.require_authentication
|
41
73
|
erb :admin, layout: false
|
42
74
|
end
|
43
75
|
|
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
@@ -16,13 +16,14 @@ module Osso
|
|
16
16
|
# Once they complete IdP login, they will be returned to the
|
17
17
|
# redirect_uri with an authorization code parameter.
|
18
18
|
get '/authorize' do
|
19
|
-
identity_providers = find_providers
|
20
|
-
|
21
19
|
validate_oauth_request(env)
|
22
20
|
|
23
|
-
|
21
|
+
return erb :hosted_login if render_hosted_login?
|
22
|
+
|
23
|
+
@providers = find_providers
|
24
|
+
|
25
|
+
return erb :saml_login_form if @providers.one?
|
24
26
|
|
25
|
-
@providers = identity_providers.not_pending
|
26
27
|
return erb :multiple_providers if @providers.count > 1
|
27
28
|
|
28
29
|
raise Osso::Error::MissingConfiguredIdentityProvider.new(domain: params[:domain])
|
@@ -61,6 +62,10 @@ module Osso
|
|
61
62
|
|
62
63
|
private
|
63
64
|
|
65
|
+
def render_hosted_login?
|
66
|
+
[params[:email], params[:domain]].all?(&:nil?)
|
67
|
+
end
|
68
|
+
|
64
69
|
def find_providers
|
65
70
|
if params[:email]
|
66
71
|
user = Osso::Models::User.
|
@@ -71,6 +76,7 @@ module Osso
|
|
71
76
|
|
72
77
|
Osso::Models::IdentityProvider.
|
73
78
|
joins(:oauth_client).
|
79
|
+
not_pending.
|
74
80
|
where(
|
75
81
|
domain: domain_from_params,
|
76
82
|
oauth_clients: { identifier: params[:client_id] },
|
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
|