sso 0.0.2 → 0.1.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sso.rb +6 -0
  3. data/spec/dummy/Rakefile +9 -0
  4. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  5. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  6. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  7. data/spec/dummy/app/controllers/home_controller.rb +4 -0
  8. data/spec/dummy/app/controllers/sessions_controller.rb +39 -0
  9. data/spec/dummy/app/models/user.rb +9 -0
  10. data/spec/dummy/app/views/home/index.html.erb +0 -0
  11. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  12. data/spec/dummy/app/views/sessions/new.html.erb +10 -0
  13. data/spec/dummy/bin/bundle +3 -0
  14. data/spec/dummy/bin/rails +4 -0
  15. data/spec/dummy/bin/rake +4 -0
  16. data/spec/dummy/bin/setup +29 -0
  17. data/spec/dummy/config.ru +4 -0
  18. data/spec/dummy/config/application.rb +36 -0
  19. data/spec/dummy/config/boot.rb +4 -0
  20. data/spec/dummy/config/database.yml +38 -0
  21. data/spec/dummy/config/environment.rb +5 -0
  22. data/spec/dummy/config/environments/development.rb +37 -0
  23. data/spec/dummy/config/environments/test.rb +42 -0
  24. data/spec/dummy/config/initializers/assets.rb +11 -0
  25. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  26. data/spec/dummy/config/initializers/doorkeeper.rb +23 -0
  27. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  28. data/spec/dummy/config/initializers/secret_token.rb +10 -0
  29. data/spec/dummy/config/initializers/session_store.rb +3 -0
  30. data/spec/dummy/config/initializers/sso.rb +37 -0
  31. data/spec/dummy/config/initializers/warden.rb +29 -0
  32. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  33. data/spec/dummy/config/locales/doorkeeper.en.yml +151 -0
  34. data/spec/dummy/config/locales/en.yml +23 -0
  35. data/spec/dummy/config/routes.rb +12 -0
  36. data/spec/dummy/db/migrate/20150302113121_add_users.rb +14 -0
  37. data/spec/dummy/db/migrate/20150303054803_create_doorkeeper_tables.rb +51 -0
  38. data/spec/dummy/db/migrate/20150303132931_create_passports_table.rb +38 -0
  39. data/spec/dummy/db/schema.rb +97 -0
  40. data/spec/integration/oauth/after_fetch_spec.rb +43 -0
  41. data/spec/integration/oauth/authorization_code_spec.rb +56 -0
  42. data/spec/integration/oauth/password_verification_spec.rb +67 -0
  43. data/spec/lib/sso/logging_spec.rb +39 -0
  44. data/spec/spec_helper.rb +54 -0
  45. data/spec/support/factories/doorkeeper/application.rb +21 -0
  46. data/spec/support/factories/server/passport.rb +10 -0
  47. data/spec/support/factories/server/user.rb +14 -0
  48. data/spec/support/sso/test.rb +9 -0
  49. data/spec/support/sso/test/cookie_stripper.rb +20 -0
  50. data/spec/support/sso/test/helpers.rb +56 -0
  51. metadata +283 -17
@@ -0,0 +1,3 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Rails.application.config.session_store :cookie_store, key: '_dummy_session'
@@ -0,0 +1,37 @@
1
+ # POI
2
+
3
+ # This is the minimal configuration you need to do for using the sso gem.
4
+
5
+ SSO.configure do |config|
6
+
7
+ config.find_user_for_passport = proc do |passport, ip|
8
+ # This is your chance to modify the user instance before it is handed out to the OAuth client apps.
9
+
10
+ progname = 'SSO.config.find_user_for_passport'
11
+ Rails.logger.debug(progname) { "Looking up User #{passport.owner_id} belonging to Passport #{passport.id} surfing with IP #{ip}..." }
12
+ user = User.find_by_id passport.owner_id
13
+ return unless user
14
+
15
+ # The IP address, for example, might be used to set certain flags on the user object.
16
+ # If these flags are included in the #user_state base (see below), all OAuth client apps are immediately aware of the change.
17
+ if ip == '198.51.100.74'
18
+ user.tags << :is_at_the_office
19
+ else
20
+ user.tags << :is_working_from_home
21
+ end
22
+
23
+ user
24
+ end
25
+
26
+ config.user_state_base = proc do |user|
27
+ # Include the end-user credentials to force all OAuth client apps to refetch the end-user Passports.
28
+ # This way you can revoke all relevant Passports on SSO-logout and the OAuth client apps are immediately aware of it.
29
+ [user.email, user.password, user.tags.sort].join
30
+ end
31
+
32
+ # This is a rather static key used to calculate whether a user state changed and needs to be propagated to the OAuth clients.
33
+ # It's not a problem if this changes, as long as it's somewhat deterministic.
34
+ # In our case, we simply derive it from the Rails secret_key_base so we don't have to remember yet another secret somewhere.
35
+ generator = ActiveSupport::KeyGenerator.new Rails.application.config.secret_key_base, iterations: 1000
36
+ config.user_state_key = Rails.application.config.user_state_digest_key = generator.generate_key 'user state digest key'
37
+ end
@@ -0,0 +1,29 @@
1
+ # POI
2
+ ::Warden::Strategies.add :password do
3
+ def valid?
4
+ params['username'].present?
5
+ end
6
+
7
+ def authenticate!
8
+ Rails.logger.debug(progname) { 'Authenticating from username and password...' }
9
+
10
+ user = ::User.authenticate params['username'], params['password']
11
+
12
+ if user
13
+ Rails.logger.debug(progname) { 'Authentication from username and password successful.' }
14
+ success! user
15
+ else
16
+ Rails.logger.debug(progname) { 'Authentication from username and password failed.' }
17
+ fail! 'Could not login.'
18
+ end
19
+ end
20
+
21
+ def progname
22
+ 'Warden::Strategies.password'
23
+ end
24
+ end
25
+
26
+ # POI
27
+ Warden::Manager.after_authentication(&::SSO::Server::Warden::Hooks::AfterAuthentication.to_proc)
28
+ Warden::Manager.before_logout(&::SSO::Server::Warden::Hooks::BeforeLogout.to_proc)
29
+ Warden::Strategies.add :passport, ::SSO::Server::Warden::Strategies::Passport
@@ -0,0 +1,14 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # This file contains settings for ActionController::ParamsWrapper which
4
+ # is enabled by default.
5
+
6
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
+ ActiveSupport.on_load(:action_controller) do
8
+ wrap_parameters format: [:json] if respond_to?(:wrap_parameters)
9
+ end
10
+
11
+ # To enable root element in JSON for ActiveRecord objects.
12
+ # ActiveSupport.on_load(:active_record) do
13
+ # self.include_root_in_json = true
14
+ # end
@@ -0,0 +1,151 @@
1
+ en:
2
+ activerecord:
3
+ attributes:
4
+ doorkeeper/application:
5
+ name: 'Name'
6
+ redirect_uri: 'Redirect URI'
7
+ errors:
8
+ models:
9
+ doorkeeper/application:
10
+ attributes:
11
+ redirect_uri:
12
+ fragment_present: 'cannot contain a fragment.'
13
+ invalid_uri: 'must be a valid URI.'
14
+ relative_uri: 'must be an absolute URI.'
15
+ secured_uri: 'must be an HTTPS/SSL URI.'
16
+
17
+ mongoid:
18
+ attributes:
19
+ doorkeeper/application:
20
+ name: 'Name'
21
+ redirect_uri: 'Redirect URI'
22
+ errors:
23
+ models:
24
+ doorkeeper/application:
25
+ attributes:
26
+ redirect_uri:
27
+ fragment_present: 'cannot contain a fragment.'
28
+ invalid_uri: 'must be a valid URI.'
29
+ relative_uri: 'must be an absolute URI.'
30
+ secured_uri: 'must be an HTTPS/SSL URI.'
31
+
32
+ mongo_mapper:
33
+ attributes:
34
+ doorkeeper/application:
35
+ name: 'Name'
36
+ redirect_uri: 'Redirect URI'
37
+ errors:
38
+ models:
39
+ doorkeeper/application:
40
+ attributes:
41
+ redirect_uri:
42
+ fragment_present: 'cannot contain a fragment.'
43
+ invalid_uri: 'must be a valid URI.'
44
+ relative_uri: 'must be an absolute URI.'
45
+ secured_uri: 'must be an HTTPS/SSL URI.'
46
+
47
+ doorkeeper:
48
+ applications:
49
+ confirmations:
50
+ destroy: 'Are you sure?'
51
+ buttons:
52
+ edit: 'Edit'
53
+ destroy: 'Destroy'
54
+ submit: 'Submit'
55
+ cancel: 'Cancel'
56
+ authorize: 'Authorize'
57
+ form:
58
+ error: 'Whoops! Check your form for possible errors'
59
+ help:
60
+ redirect_uri: 'Use one line per URI'
61
+ native_redirect_uri: 'Use %{native_redirect_uri} for local tests'
62
+ edit:
63
+ title: 'Edit application'
64
+ index:
65
+ title: 'Your applications'
66
+ new: 'New Application'
67
+ name: 'Name'
68
+ callback_url: 'Callback URL'
69
+ new:
70
+ title: 'New Application'
71
+ show:
72
+ title: 'Application: %{name}'
73
+ application_id: 'Application Id'
74
+ secret: 'Secret'
75
+ callback_urls: 'Callback urls'
76
+ actions: 'Actions'
77
+
78
+ authorizations:
79
+ buttons:
80
+ authorize: 'Authorize'
81
+ deny: 'Deny'
82
+ error:
83
+ title: 'An error has occurred'
84
+ new:
85
+ title: 'Authorize required'
86
+ prompt: 'Authorize %{client_name} to use your account?'
87
+ able_to: 'This application will be able to'
88
+ show:
89
+ title: 'Authorization code'
90
+
91
+ authorized_applications:
92
+ confirmations:
93
+ revoke: 'Are you sure?'
94
+ buttons:
95
+ revoke: 'Revoke'
96
+ index:
97
+ title: 'Your authorized applications'
98
+ application: 'Application'
99
+ created_at: 'Created At'
100
+ date_format: '%Y-%m-%d %H:%M:%S'
101
+
102
+ errors:
103
+ messages:
104
+ # Common error messages
105
+ invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.'
106
+ invalid_redirect_uri: 'The redirect uri included is not valid.'
107
+ unauthorized_client: 'The client is not authorized to perform this request using this method.'
108
+ access_denied: 'The resource owner or authorization server denied the request.'
109
+ invalid_scope: 'The requested scope is invalid, unknown, or malformed.'
110
+ server_error: 'The authorization server encountered an unexpected condition which prevented it from fulfilling the request.'
111
+ temporarily_unavailable: 'The authorization server is currently unable to handle the request due to a temporary overloading or maintenance of the server.'
112
+
113
+ #configuration error messages
114
+ credential_flow_not_configured: 'Resource Owner Password Credentials flow failed due to Doorkeeper.configure.resource_owner_from_credentials being unconfigured.'
115
+ resource_owner_authenticator_not_configured: 'Resource Owner find failed due to Doorkeeper.configure.resource_owner_authenticator being unconfiged.'
116
+
117
+ # Access grant errors
118
+ unsupported_response_type: 'The authorization server does not support this response type.'
119
+
120
+ # Access token errors
121
+ invalid_client: 'Client authentication failed due to unknown client, no client authentication included, or unsupported authentication method.'
122
+ invalid_grant: 'The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.'
123
+ unsupported_grant_type: 'The authorization grant type is not supported by the authorization server.'
124
+
125
+ # Password Access token errors
126
+ invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found'
127
+
128
+ invalid_token:
129
+ revoked: "The access token was revoked"
130
+ expired: "The access token expired"
131
+ unknown: "The access token is invalid"
132
+
133
+ flash:
134
+ applications:
135
+ create:
136
+ notice: 'Application created.'
137
+ destroy:
138
+ notice: 'Application deleted.'
139
+ update:
140
+ notice: 'Application updated.'
141
+ authorized_applications:
142
+ destroy:
143
+ notice: 'Application revoked.'
144
+
145
+ layouts:
146
+ admin:
147
+ nav:
148
+ oauth2_provider: 'OAuth2 Provider'
149
+ applications: 'Applications'
150
+ application:
151
+ title: 'OAuth authorize required'
@@ -0,0 +1,23 @@
1
+ # Files in the config/locales directory are used for internationalization
2
+ # and are automatically loaded by Rails. If you want to use locales other
3
+ # than English, add the necessary files in this directory.
4
+ #
5
+ # To use the locales, use `I18n.t`:
6
+ #
7
+ # I18n.t 'hello'
8
+ #
9
+ # In views, this is aliased to just `t`:
10
+ #
11
+ # <%= t('hello') %>
12
+ #
13
+ # To use a different locale, set it with `I18n.locale`:
14
+ #
15
+ # I18n.locale = :es
16
+ #
17
+ # This would use the information in config/locales/es.yml.
18
+ #
19
+ # To learn more, please read the Rails Internationalization guide
20
+ # available at http://guides.rubyonrails.org/i18n.html.
21
+
22
+ en:
23
+ hello: "Hello world"
@@ -0,0 +1,12 @@
1
+ Rails.application.routes.draw do
2
+
3
+ use_doorkeeper do
4
+ skip_controllers :applications, :authorized_applications
5
+ end
6
+
7
+ resources :sessions, only: [:new, :create]
8
+ get '/logout(/:passport_id)', to: 'sessions#logout', as: :logout
9
+
10
+ root to: 'home#index'
11
+
12
+ end
@@ -0,0 +1,14 @@
1
+ # This is not part of SSO, this is simply an example implementation of a user model.
2
+
3
+ class AddUsers < ActiveRecord::Migration
4
+ def change
5
+ create_table :users do |t|
6
+ t.string :name, null: false
7
+ t.string :email, null: false
8
+ t.string :password, null: false # <- Of course you would have this encrypted in a real-life setup
9
+ t.string :tags, array: true, default: []
10
+ t.boolean :vip
11
+ t.timestamps null: false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ # POI
2
+
3
+ # This migration file is created when you run `rails generate doorkeeper:install` in your OAuth Server Rails app.
4
+ # These tables are needed for Doorkeeper to work, see also https://github.com/doorkeeper-gem/doorkeeper#installation
5
+ # This migration here has *not* been modified. You simply see the original file below, created by the Doorkeeper generator.
6
+
7
+ # Note, however, that it was generated with Doorkeeper >= 2.0.0, since it has the `scopes` column on the `oauth_applications` table.
8
+ # We make use of that column and it was introduced here: https://github.com/doorkeeper-gem/doorkeeper/blob/master/CHANGELOG.md#200
9
+
10
+ class CreateDoorkeeperTables < ActiveRecord::Migration
11
+ def change
12
+ create_table :oauth_applications do |t|
13
+ t.string :name, null: false
14
+ t.string :uid, null: false
15
+ t.string :secret, null: false
16
+ t.text :redirect_uri, null: false
17
+ t.string :scopes, null: false, default: '' # <- Exists only with Doorkeeper 2.0.0 or higher.
18
+ t.timestamps null: false
19
+ end
20
+
21
+ add_index :oauth_applications, :uid, unique: true
22
+
23
+ create_table :oauth_access_grants do |t|
24
+ t.integer :resource_owner_id, null: false
25
+ t.integer :application_id, null: false
26
+ t.string :token, null: false
27
+ t.integer :expires_in, null: false
28
+ t.text :redirect_uri, null: false
29
+ t.datetime :created_at, null: false
30
+ t.datetime :revoked_at
31
+ t.string :scopes
32
+ end
33
+
34
+ add_index :oauth_access_grants, :token, unique: true
35
+
36
+ create_table :oauth_access_tokens do |t|
37
+ t.integer :resource_owner_id
38
+ t.integer :application_id
39
+ t.string :token, null: false
40
+ t.string :refresh_token
41
+ t.integer :expires_in
42
+ t.datetime :revoked_at
43
+ t.datetime :created_at, null: false
44
+ t.string :scopes
45
+ end
46
+
47
+ add_index :oauth_access_tokens, :token, unique: true
48
+ add_index :oauth_access_tokens, :resource_owner_id
49
+ add_index :oauth_access_tokens, :refresh_token, unique: true
50
+ end
51
+ end
@@ -0,0 +1,38 @@
1
+ # POI
2
+
3
+ # This is what the Passport table on the SSO Server looks like. You need to have this migration.
4
+ # As you can see it uses the `uuid` and `inet` column types. So you are kind of stuck with Postgres.
5
+ # However, there should be no reason for you not to simply use `integer` and `string` for those two columns instead.
6
+
7
+ class CreatePassportsTable < ActiveRecord::Migration
8
+ def change
9
+ enable_extension 'uuid-ossp'
10
+
11
+ create_table :passports, id: :uuid do |t|
12
+ t.integer :oauth_access_grant_id
13
+ t.integer :oauth_access_token_id
14
+ t.integer :application_id, null: false
15
+ t.integer :owner_id, null: false
16
+ t.string :group_id, null: false
17
+ t.string :secret, null: false, unique: true
18
+ t.inet :ip, null: false
19
+ t.string :agent
20
+ t.string :location
21
+ t.datetime :activity_at, null: false
22
+ t.datetime :revoked_at
23
+ t.string :revoke_reason
24
+ t.timestamps null: false
25
+ end
26
+
27
+ add_index :passports, [:owner_id, :oauth_access_token_id], where: 'revoked_at IS NULL AND oauth_access_token_id IS NOT NULL', unique: true, name: :one_access_token_per_owner
28
+
29
+ add_index :passports, :oauth_access_grant_id
30
+ add_index :passports, :oauth_access_token_id
31
+ add_index :passports, :application_id
32
+ add_index :passports, :owner_id
33
+ add_index :passports, :group_id
34
+ add_index :passports, :secret
35
+ add_index :passports, :ip
36
+ add_index :passports, :revoke_reason
37
+ end
38
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 20150303132931) do
15
+
16
+ # These are extensions that must be enabled in order to support this database
17
+ enable_extension "plpgsql"
18
+ enable_extension "uuid-ossp"
19
+
20
+ create_table "oauth_access_grants", force: :cascade do |t|
21
+ t.integer "resource_owner_id", null: false
22
+ t.integer "application_id", null: false
23
+ t.string "token", null: false
24
+ t.integer "expires_in", null: false
25
+ t.text "redirect_uri", null: false
26
+ t.datetime "created_at", null: false
27
+ t.datetime "revoked_at"
28
+ t.string "scopes"
29
+ end
30
+
31
+ add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
32
+
33
+ create_table "oauth_access_tokens", force: :cascade do |t|
34
+ t.integer "resource_owner_id"
35
+ t.integer "application_id"
36
+ t.string "token", null: false
37
+ t.string "refresh_token"
38
+ t.integer "expires_in"
39
+ t.datetime "revoked_at"
40
+ t.datetime "created_at", null: false
41
+ t.string "scopes"
42
+ end
43
+
44
+ add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
45
+ add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
46
+ add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
47
+
48
+ create_table "oauth_applications", force: :cascade do |t|
49
+ t.string "name", null: false
50
+ t.string "uid", null: false
51
+ t.string "secret", null: false
52
+ t.text "redirect_uri", null: false
53
+ t.string "scopes", default: "", null: false
54
+ t.datetime "created_at", null: false
55
+ t.datetime "updated_at", null: false
56
+ end
57
+
58
+ add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
59
+
60
+ create_table "passports", id: :uuid, default: "uuid_generate_v4()", force: :cascade do |t|
61
+ t.integer "oauth_access_grant_id"
62
+ t.integer "oauth_access_token_id"
63
+ t.integer "application_id", null: false
64
+ t.integer "owner_id", null: false
65
+ t.string "group_id", null: false
66
+ t.string "secret", null: false
67
+ t.inet "ip", null: false
68
+ t.string "agent"
69
+ t.string "location"
70
+ t.datetime "activity_at", null: false
71
+ t.datetime "revoked_at"
72
+ t.string "revoke_reason"
73
+ t.datetime "created_at", null: false
74
+ t.datetime "updated_at", null: false
75
+ end
76
+
77
+ add_index "passports", ["application_id"], name: "index_passports_on_application_id", using: :btree
78
+ add_index "passports", ["group_id"], name: "index_passports_on_group_id", using: :btree
79
+ add_index "passports", ["ip"], name: "index_passports_on_ip", using: :btree
80
+ add_index "passports", ["oauth_access_grant_id"], name: "index_passports_on_oauth_access_grant_id", using: :btree
81
+ add_index "passports", ["oauth_access_token_id"], name: "index_passports_on_oauth_access_token_id", using: :btree
82
+ add_index "passports", ["owner_id", "oauth_access_token_id"], name: "one_access_token_per_owner", unique: true, where: "((revoked_at IS NULL) AND (oauth_access_token_id IS NOT NULL))", using: :btree
83
+ add_index "passports", ["owner_id"], name: "index_passports_on_owner_id", using: :btree
84
+ add_index "passports", ["revoke_reason"], name: "index_passports_on_revoke_reason", using: :btree
85
+ add_index "passports", ["secret"], name: "index_passports_on_secret", using: :btree
86
+
87
+ create_table "users", force: :cascade do |t|
88
+ t.string "name", null: false
89
+ t.string "email", null: false
90
+ t.string "password", null: false
91
+ t.string "tags", default: [], array: true
92
+ t.boolean "vip"
93
+ t.datetime "created_at", null: false
94
+ t.datetime "updated_at", null: false
95
+ end
96
+
97
+ end