anoubis_sso_server 0.1.1 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,337 @@
1
+ ##
2
+ # OpenID controller class. Defines any OpenID actions according by specification.
3
+ class AnoubisSsoServer::OpenIdController < AnoubisSsoServer::ApplicationController
4
+
5
+ ##
6
+ # Action returns {https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationResponse Provider OpenID configuration}.
7
+ #
8
+ # Default path: /openid/.well-known/openid-configuration
9
+ # @return [Hash] Current OpenID configuration
10
+ def configuration
11
+ result = {
12
+ issuer: sso_server + 'openid/',
13
+ authorization_endpoint: sso_server + 'openid/oauth2/auth',
14
+ token_endpoint: sso_server + 'openid/oauth2/token',
15
+ jwks_uri: sso_server + 'openid/.well-known/jwks.json',
16
+ subject_types_supported: %w[public],
17
+ #response_types_supported: ['code', 'code id_token', 'id_token', 'token id_token', 'token', 'token id_token code'],
18
+ response_types_supported: %w[code],
19
+ claims_supported: %w[sub],
20
+ #grant_types_supported: ['authorization_code', 'implicit', 'client_credentials', 'refresh_token'],
21
+ grant_types_supported: %w[authorization_code],
22
+ response_modes_supported: %w[query fragment],
23
+ userinfo_endpoint: sso_server + 'openid/userinfo',
24
+ scopes_supported: %w[offline_access offline openid'],
25
+ token_endpoint_auth_methods_supported: %w[client_secret_post client_secret_basic private_key_jwt none],
26
+ userinfo_signing_alg_values_supported: %w[none RS256],
27
+ id_token_signing_alg_values_supported: %w[RS256],
28
+ request_parameter_supported: true,
29
+ request_uri_parameter_supported: true,
30
+ require_request_uri_registration: true,
31
+ claims_parameter_supported: false,
32
+ revocation_endpoint: sso_server + 'openid/oauth2/revoke',
33
+ backchannel_logout_supported: true,
34
+ backchannel_logout_session_supported: true,
35
+ frontchannel_logout_supported: true,
36
+ frontchannel_logout_session_supported: true,
37
+ end_session_endpoint: sso_server + 'openid/oauth2/sessions/logout',
38
+ request_object_signing_alg_values_supported: %w[RS256 none],
39
+ code_challenge_methods_supported: %w[plain S256]
40
+ }
41
+
42
+ render json: result
43
+ end
44
+
45
+ ##
46
+ # Action returns OpenID JWKs.
47
+ #
48
+ # Default path: /openid/.well-known/jwks.json
49
+ # @return [Hash] Current JWKs
50
+ def jwks
51
+ begin
52
+ jwks_cache = JSON.parse(self.redis.get("#{redis_prefix}jwks"),{ symbolize_names: true })
53
+ rescue StandardError => e
54
+ jwks_cache = generate_jwks
55
+ end
56
+
57
+ redis.set "#{redis_prefix}jwks", jwks_cache.to_json, ex: 3600
58
+
59
+ render json: jwks_cache
60
+ end
61
+
62
+ ##
63
+ # Action for check user authorization for current browser.
64
+ def auth
65
+ result = {
66
+ result: -1
67
+ }
68
+
69
+ params[:prompt] = 'is' unless params.key? :prompt
70
+ params[:prompt] = 'is' if params[:prompt] != 'none'
71
+
72
+ err = check_basic_parameters
73
+
74
+ if err
75
+ result[:message] = err
76
+ return render(json: result)
77
+ end
78
+
79
+ sign = params[:redirect_uri].index('?') ? '&' : '?'
80
+
81
+ err = check_listed_parameters %w[response_type scope code_challenge code_challenge_method state]
82
+
83
+ if err
84
+ result[:message] = err
85
+ return if redirect_to_uri result[:message], sign
86
+ return render(json: result)
87
+ end
88
+
89
+ unless %w[code].include? params[:response_type]
90
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'response_type')
91
+ return if redirect_to_uri result[:message], sign
92
+ return render(json: result)
93
+ end
94
+
95
+ scopes = params[:scope].split(' ')
96
+
97
+ params[:code_challenge_method] = params[:code_challenge_method].downcase
98
+ unless %w[s256].include? params[:code_challenge_method]
99
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'code_challenge_method')
100
+ return if self.redirect_to_uri result[:message], sign
101
+ return render(json: result)
102
+ end
103
+
104
+ if params[:state].length < 6
105
+ result[:message] = I18n.t('anoubis.errors.less_than', title: 'state', size: 6)
106
+ return if self.redirect_to_uri result[:message], sign
107
+ return render(json: result)
108
+ end
109
+
110
+ original_url = request.url[8..]
111
+ original_url = original_url[(original_url.index('/') + 1)..]
112
+
113
+ code = SecureRandom.uuid
114
+ code_hash = {
115
+ scope: scopes,
116
+ code_challenge: params[:code_challenge],
117
+ request_uri: params[:redirect_uri],
118
+ state: params[:state],
119
+ client_id: params[:client_id],
120
+ original_url: sso_server + original_url
121
+ }
122
+
123
+ session = self.get_oauth_session
124
+
125
+ if session
126
+ user = get_user_by_uuid session[:uuid]
127
+
128
+ if user
129
+ code_hash[:uuid] = user.uuid
130
+ redis.set("#{redis_prefix}code:#{code}", code_hash.to_json, ex: 6000)
131
+ redirect_to "#{params[:redirect_uri]}#{sign}state=#{params[:state]}&scope=#{params[:scope]}&code=#{code}", { allow_other_host: true }
132
+ return
133
+ else
134
+ redis.del("#{redis_prefix}session:#{cookies[:oauth_session]}")
135
+ cookies[:oauth_session] = nil
136
+ end
137
+ end
138
+
139
+ result[:message] = I18n.t('anoubis.errors.login_required')
140
+
141
+ if params[:prompt] == 'none'
142
+ redirect_to params[:redirect_uri] + sign + 'error=' + ERB::Util.url_encode(result[:message]), { allow_other_host: true }
143
+ return
144
+ end
145
+
146
+ url = sso_login_url
147
+ url += url.index('?') ? '&' : '?'
148
+ redis.set("#{redis_prefix}login_code:#{code}", code_hash.to_json, ex: 3600)
149
+ redirect_to "#{url}code=#{code}", { allow_other_host: true }
150
+ end
151
+
152
+ ##
153
+ # Action makes access token based on defined parameters
154
+ def access_token
155
+ result = {
156
+ result: -1
157
+ }
158
+
159
+ params[:prompt] == 'yes'
160
+
161
+ err = check_basic_parameters
162
+
163
+ if err
164
+ result[:message] = err
165
+ return render(json: result)
166
+ end
167
+
168
+ err = check_listed_parameters %w[scope code code_verifier grant_type]
169
+
170
+ if err
171
+ result[:message] = err
172
+ return render(json: result)
173
+ end
174
+
175
+ begin
176
+ code = JSON.parse(redis.get("#{redis_prefix}code:#{params[:code]}"),{ symbolize_names: true })
177
+ rescue
178
+ code = nil
179
+ end
180
+
181
+ if !code || code.class != Hash
182
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'code')
183
+ return render(json: result)
184
+ end
185
+
186
+ str = Digest::SHA256.base64digest(params[:code_verifier]).tr("+/", "-_").tr("=", "")
187
+
188
+ if code[:code_challenge] != str
189
+ result[:message] = I18n.t('anoubis.errors.is_not_correct', title: 'code_verifier')
190
+ return render(json: result)
191
+ end
192
+
193
+ if code[:request_uri] != params[:redirect_uri]
194
+ result[:error] = I18n.t('anoubis.errors.is_not_correct', title: 'request_uri')
195
+ return render(json: result)
196
+ end
197
+
198
+ header = {
199
+ alg: "RS256",
200
+ kid: "public:#{current_system.public}",
201
+ typ: "JWT"
202
+ }
203
+
204
+ user = get_user_by_uuid code[:uuid]
205
+
206
+ payload = {
207
+ aud: [],
208
+ client_id: current_system.uuid,
209
+ exp: Time.now.utc.to_i + current_system.ttl,
210
+ ext: {},
211
+ iat: Time.now.utc.to_i,
212
+ nbf: Time.now.utc.to_i,
213
+ iss: "#{sso_server}openid/",
214
+ jti: SecureRandom.uuid,
215
+ sub: SecureRandom.uuid,
216
+ scp: []
217
+ }
218
+
219
+ keys = JWT::JWK.import(current_system.jwk)
220
+
221
+ user_payload = {
222
+ aud: [current_system.public],
223
+ auth_time: Time.now.utc.to_i,
224
+ exp: Time.now.utc.to_i + current_system.ttl,
225
+ iss: "#{sso_server}openid/",
226
+ jti: SecureRandom.uuid,
227
+ sid: SecureRandom.uuid,
228
+ sub: SecureRandom.uuid,
229
+ iat: Time.now.utc.to_i,
230
+ rat: Time.now.utc.to_i - 1
231
+ }
232
+
233
+ user_payload[:email] = user.email if code[:scope].include? 'email'
234
+
235
+ if code[:scope].include? 'profile'
236
+ user_payload[:name] = user.name
237
+ user_payload[:surname] = user.surname
238
+ end
239
+
240
+ result = {
241
+ access_token: JWT.encode(payload, keys.keypair, 'RS256', header),
242
+ expires_in: current_system.ttl,
243
+ scope: code[:scope],
244
+ token_type: 'bearer',
245
+ id_token: JWT.encode(user_payload, keys.keypair, 'RS256', header),
246
+ }
247
+
248
+ token_hash = {
249
+ uuid: user.uuid
250
+ }
251
+
252
+ self.redis.set("#{redis_prefix}token:#{result[:access_token]}", token_hash.to_json, ex: current_system.ttl)
253
+ self.redis.del("#{redis_prefix}code:#{params[:code]}")
254
+
255
+ options
256
+
257
+ render json: result
258
+ end
259
+
260
+ ##
261
+ # Clear default session
262
+ def logout
263
+ redis.del("#{redis_prefix}session:#{cookies[:oauth_session]}")
264
+ cookies[:oauth_session] = nil
265
+ redirect_to sso_login_url, { allow_other_host: true }
266
+ end
267
+
268
+ ##
269
+ # Check basic oauth parameters (client_id, redirect_uri)
270
+ def check_basic_parameters
271
+ return I18n.t('anoubis.errors.is_not_defined', title: 'client_id') unless params.key? :client_id
272
+
273
+ @current_system = self.get_current_system params[:client_id]
274
+
275
+ return I18n.t('anoubis.errors.is_not_correct', title: 'client_id') unless current_system
276
+
277
+ return I18n.t('anoubis.errors.is_not_defined', title: 'redirect_uri') unless params.key? :redirect_uri
278
+
279
+ return I18n.t('anoubis.errors.is_not_correct', title: 'redirect_uri') unless current_system.request_uri.include? params[:redirect_uri]
280
+
281
+ nil
282
+ end
283
+
284
+ ##
285
+ # Check parameters
286
+ # @param list [Array] Array of parameters to check
287
+ def check_listed_parameters(list)
288
+ list.each do |key|
289
+ return I18n.t('anoubis.errors.is_not_defined', title: key) unless params.key? key.to_sym
290
+
291
+ return I18n.t('anoubis.errors.is_not_correct', title: key) unless params[key.to_sym]
292
+
293
+ params[key.to_sym].strip!
294
+
295
+ return I18n.t('anoubis.errors.is_not_correct', title: key) if params[key.to_sym] == ''
296
+ end
297
+
298
+ nil
299
+ end
300
+
301
+ ##
302
+ # Check if page should be redirected to url
303
+ # @param error [String] Error message
304
+ # @param sign [String] Redirect url sign (? or &)
305
+ # @return [Boolean] return 'true' if page should be redirected
306
+ def redirect_to_uri(error, sign)
307
+ if params[:prompt] == 'none'
308
+ redirect_to params[:redirect_uri] + sign + 'error=' + ERB::Util.url_encode(error), { allow_other_host: true }
309
+ return true
310
+ end
311
+
312
+ false
313
+ end
314
+
315
+ ##
316
+ # Procedure generates keys according by used systems. Data is loaded from {AnoubisSsoServer::System}.
317
+ # @return [Hash] Hash ow JWK keys
318
+ def generate_jwks
319
+ result = {
320
+ keys: []
321
+ }
322
+
323
+ AnoubisSsoServer::System.where(state: 'opened').each do |sys|
324
+ key = {
325
+ use: 'sig',
326
+ kty: sys.jwk[:kty],
327
+ kid: "public:#{sys.uuid}",
328
+ alg: 'RS256',
329
+ n: sys.jwk[:n],
330
+ e: sys.jwk[:e]
331
+ }
332
+ result[:keys].push key
333
+ end
334
+
335
+ result
336
+ end
337
+ end
@@ -1,2 +1,5 @@
1
- class ApplicationRecord < Anoubis::ApplicationRecord
1
+ ##
2
+ # Main Active Record object inherited from {https://www.rubydoc.info/gems/anoubis/Anoubis/ApplicationRecord Anoubis::ApplicationRecord}
3
+ class AnoubisSsoServer::ApplicationRecord < Anoubis::ApplicationRecord
4
+ self.abstract_class = true
2
5
  end
@@ -0,0 +1,68 @@
1
+ ##
2
+ # Default System class
3
+ class AnoubisSsoServer::System < AnoubisSsoServer::ApplicationRecord
4
+ self.table_name = 'systems'
5
+
6
+ before_validation :before_validation_sso_server_system_on_create, on: :create
7
+ before_save :before_save_sso_server_system
8
+ after_save :after_save_sso_server_system
9
+ after_destroy :after_destroy_sso_server_system
10
+
11
+ validates :title, presence: true, length: { maximum: 100 }
12
+ validates :uuid, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
13
+ validates :public, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
14
+
15
+ enum state: { opened: 0, hidden: 1 }
16
+
17
+ ##
18
+ # Fires before create system. Procedure generates public and private UUID and RSA keypair
19
+ def before_validation_sso_server_system_on_create
20
+ self.uuid = setup_private_system_id
21
+ self.public = setup_public_system_id unless public
22
+ self.request_uri = [] unless request_uri
23
+
24
+ keys = JWT::JWK.new(OpenSSL::PKey::RSA.new(2048))
25
+ self.jwk = keys.export include_private: true
26
+ end
27
+
28
+ ##
29
+ # Procedure setup private user identifier. Procedure can be redefined.
30
+ # @return [String] public user identifier
31
+ def setup_private_system_id
32
+ SecureRandom.uuid
33
+ end
34
+
35
+ ##
36
+ # Procedure setup public user identifier. Used for open API. Procedure can be redefined.
37
+ # @return [String] public user identifier
38
+ def setup_public_system_id
39
+ SecureRandom.uuid
40
+ end
41
+
42
+ ##
43
+ # Returns JWK information
44
+ # @return [Hash] JWK information
45
+ def jwk
46
+ @jwk ||= super.deep_symbolize_keys!
47
+ end
48
+
49
+ ##
50
+ # Fires before System was saved to SQL database. Delete old system cache if public identifier was changed.
51
+ def before_save_sso_server_system
52
+ redis.del "#{redis_prefix}system:#{public_was}" if public_was && public != public_was
53
+ end
54
+
55
+ ##
56
+ # Fires after System was saved to SQL database and setup system cache in Redis database for fast access.
57
+ def after_save_sso_server_system
58
+ redis.set("#{redis_prefix}system:#{public}", { uuid: uuid, public: public, request_uri: request_uri, jwk: jwk, ttl: ttl }.to_json )
59
+ redis.del "#{redis_prefix}jwks"
60
+ end
61
+
62
+ ##
63
+ # Fires after System was destroyed. Clears all systems caches.
64
+ def after_destroy_sso_server_system
65
+ redis.del "#{redis_prefix}system:#{public}"
66
+ redis.del "#{redis_prefix}jwks"
67
+ end
68
+ end
@@ -1,13 +1,16 @@
1
- class AnoubisSsoServer::User < ApplicationRecord
1
+ ##
2
+ # Default user class
3
+ class AnoubisSsoServer::User < AnoubisSsoServer::ApplicationRecord
2
4
  self.table_name = 'users'
3
5
 
4
6
  has_secure_password
5
7
  validates :email, presence: true
6
8
 
7
- before_validation :before_validation_user_on_create, on: :create
8
- before_save :before_save_user
9
- after_destroy :after_destroy_user
9
+ before_validation :before_validation_sso_server_user_on_create, on: :create
10
+ before_save :before_save_sso_server_user
11
+ after_destroy :after_destroy_sso_server_user
10
12
 
13
+ ## Regexp validation mask for email
11
14
  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
12
15
 
13
16
  validates :email, presence: true, length: { maximum: 50 },
@@ -25,46 +28,83 @@ class AnoubisSsoServer::User < ApplicationRecord
25
28
  validates :uuid, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
26
29
  validates :public, presence: true, length: { maximum: 40 }, uniqueness: { case_sensitive: true }
27
30
 
28
- def before_validation_user_on_create
29
- self.uuid = SecureRandom.uuid
30
- self.public = SecureRandom.uuid
31
+ ##
32
+ # Fires before create any User on the server. Procedure generates internal UUID and setup timezone to GMT.
33
+ # Public user identifier is generated also if not defined.
34
+ def before_validation_sso_server_user_on_create
35
+ self.uuid = setup_private_user_id
36
+ self.public = setup_public_user_id unless public
37
+
31
38
  self.timezone = 'GMT' if !self.timezone
32
39
  end
33
40
 
34
- def before_save_user
41
+ ##
42
+ # Procedure setup private user identifier. Procedure can be redefined.
43
+ # @return [String] public user identifier
44
+ def setup_private_user_id
45
+ SecureRandom.uuid
46
+ end
47
+
48
+ ##
49
+ # Procedure setup public user identifier. Used for open API. Procedure can be redefined.
50
+ # @return [String] public user identifier
51
+ def setup_public_user_id
52
+ SecureRandom.uuid
53
+ end
54
+
55
+ ##
56
+ # Fires before save User data to database.
57
+ # Procedure setup email, timezone and call procedure for clear Redis cache data for the user.
58
+ def before_save_sso_server_user
35
59
  self.timezone = 'GMT' if !self.timezone
36
60
  self.email = self.email.downcase
37
61
  self.clear_cache
38
62
  end
39
63
 
40
- def after_destroy_user
41
- self.clear_cache
64
+ ##
65
+ # Fires before delete User from database.
66
+ # Procedure call procedure for clear Redis cache data for the user.
67
+ def after_destroy_sso_server_user
68
+ clear_cache
42
69
  end
43
70
 
71
+ ##
72
+ # Procedure saves cached User model data to Redis database for improve access speed.
44
73
  def save_cache
45
- self.redis.set(self.redis_prefix + 'user:' + self.uuid, self.to_json(except: [:password_digest])) if self.redis
74
+ redis.set("#{redis_prefix}user:#{uuid}", self.to_json(except: [:password_digest])) if redis
46
75
  end
47
76
 
77
+ ##
78
+ # Procedure clear cached User model data.
48
79
  def clear_cache
49
- self.redis.del(self.redis_prefix + 'user:' + self.uuid) if self.redis
80
+ redis.del("#{redis_prefix}user:#{uuid}") if redis
50
81
  end
51
82
 
83
+ ##
84
+ # Procedure checks if password was changed.
85
+ # @return [Boolean] return true if password was changed
52
86
  def password_changed?
53
87
  !password.blank?
54
88
  end
55
89
 
56
- def self.load_cache(redis, uuid)
90
+ ##
91
+ # Procedure returns User model data from the cache or database. If user data isn't present in cache then call procedure that cache User model data.
92
+ # @param uuid [String] - User private UUID
93
+ # @return [Hash] User data
94
+ def self.load_cache(uuid)
57
95
  begin
58
- data = JSON.parse redis.get(User.redis_prefix + 'user:' + uuid), { symbolize_names: true }
96
+ data = JSON.parse self.redis.get("#{self.redis_prefix}user:#{uuid}"), { symbolize_names: true }
59
97
  rescue
60
98
  data = nil
61
99
  end
62
100
 
63
101
  unless data
64
102
  user = self.where(uuid: uuid).first
65
- if user
66
- return JSON.parse(user.to_json(except: [:password_digest]), { symbolize_names: true })
67
- end
103
+
104
+ return nil unless user
105
+
106
+ user.save_cache
107
+ data = JSON.parse(user.to_json(except: [:password_digest]), { symbolize_names: true })
68
108
  end
69
109
 
70
110
  data
@@ -0,0 +1,14 @@
1
+ en:
2
+ anoubis:
3
+ errors:
4
+ incorrect_login: "Incorrect login or password"
5
+ system_not_defined: "SSO system is not defined in Rails.configuration.anoubis_sso_system"
6
+ session_expired: "Session expired"
7
+ incorrect_user: "Incorrect user"
8
+ is_not_defined: "%{title} isn't defined"
9
+ is_not_correct: "%{title} isn't correct"
10
+ less_than: "%{title} length should be %{size} or more symbols"
11
+ login_required: "Login required"
12
+ fields:
13
+ login: "Login not defined"
14
+ password: "Password not defined"
@@ -0,0 +1,14 @@
1
+ ru:
2
+ anoubis:
3
+ errors:
4
+ incorrect_login: "Некорректный логин или пароль"
5
+ system_not_defined: "SSO система не определена в Rails.configuration.anoubis_sso_system"
6
+ session_expired: "Сессия завершена"
7
+ incorrect_user: "Некорректный пользователь"
8
+ is_not_defined: "Переменная %{title} не определена"
9
+ is_not_correct: "Переменная %{title} некорректна"
10
+ less_than: "Длина переменной %{title} должна быть %{size} или более символов"
11
+ login_required: "Требуется логин"
12
+ fields:
13
+ login: "Логин не задан"
14
+ password: "Пароль не задан"
data/config/routes.rb ADDED
@@ -0,0 +1,23 @@
1
+ ##
2
+ # Defines default routes
3
+ AnoubisSsoServer::Engine.routes.draw do
4
+ Rails.application.routes.draw do
5
+ scope path: 'api', defaults: { format: 'json' } do
6
+ scope path: ':version' do
7
+ get 'login', to: 'anoubis_sso_server/main#login', as: 'api_internal_login'
8
+ #get 'auth', to: 'main#auth'
9
+ get 'dashboard', to: 'anoubis_sso_server/index#dashboard'
10
+ get 'menu', to: 'anoubis_sso_server/index#menu'
11
+ end
12
+ end
13
+
14
+ scope path: 'openid', defaults: { format: 'json' } do
15
+ get '.well-known/openid-configuration', to: 'anoubis_sso_server/open_id#configuration', as: 'openid_configuration'
16
+ get '.well-known/jwks.json', to: 'anoubis_sso_server/open_id#jwks', as: 'openid_jwks'
17
+ get 'oauth2/auth', to: 'anoubis_sso_server/open_id#auth', as: 'oauth_auth'
18
+ post 'oauth2/token', to: 'anoubis_sso_server/open_id#access_token', as: 'oauth_token'
19
+ options 'oauth2/token', to: 'anoubis_sso_server/application#options', as: nil
20
+ get 'oauth2/logout', to: 'anoubis_sso_server/open_id#logout', as: 'oauth_logout'
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ class CreateAnoubisSsoServerUsers < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :users do |t|
4
+ t.string :email, limit: 100, null: false
5
+ t.string :name, limit: 100, null: false
6
+ t.string :surname, limit: 100, null: false
7
+ t.string :timezone, limit: 30, null: false
8
+ t.string :locale, limit: 10, null: false, default: 'ru-RU'
9
+ t.string :password_digest, limit: 60, null: false
10
+ t.string :uuid, limit: 40, null: false
11
+ t.string :public, limit: 40, null: false
12
+
13
+ t.timestamps
14
+ end
15
+ add_index :users, [:email], unique: true
16
+ add_index :users, [:uuid], unique: true
17
+ add_index :users, [:public], unique: true
18
+ end
19
+ end
@@ -0,0 +1,17 @@
1
+ class CreateAnoubisSsoServerSystems < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :systems do |t|
4
+ t.string :title, limit: 100, null: false
5
+ t.string :uuid, limit: 40, null: false
6
+ t.string :public, limit: 40, null: false
7
+ t.integer :ttl, default: 3600, null: false
8
+ t.integer :state, default: 0, null: false
9
+ t.json :jwk
10
+ t.json :request_uri
11
+
12
+ t.timestamps
13
+ end
14
+ add_index :systems, [:uuid], unique: true
15
+ add_index :systems, [:public], unique: true
16
+ end
17
+ end
@@ -1,6 +1,6 @@
1
1
  module AnoubisSsoServer
2
2
  ##
3
- # Main AnubisSsoServer Engine class
3
+ # Main AnoubisSsoServer Engine class
4
4
  class Engine < ::Rails::Engine
5
5
  isolate_namespace AnoubisSsoServer
6
6
  config.generators.api_only = true
@@ -2,5 +2,5 @@
2
2
 
3
3
  module AnoubisSsoServer
4
4
  ## Library version
5
- VERSION = "0.1.1"
5
+ VERSION = "1.0.1"
6
6
  end
@@ -1,4 +1,10 @@
1
1
  module AnoubisSsoServer
2
2
  VERSION: String
3
3
  # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+
5
+ class User
6
+ attr_accessor email: String
7
+ attr_accessor name: String
8
+ attr_accessor surname: String
9
+ end
4
10
  end