anoubis_sso_server 0.1.1 → 1.0.5

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.
@@ -0,0 +1,373 @@
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.uuid}",
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
+ redis.set("#{redis_prefix}token:#{result[:access_token]}", token_hash.to_json, ex: current_system.ttl)
253
+ 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
+ # Action that returns user information parameters
270
+ def userinfo
271
+ auth_token = request.env.fetch('HTTP_AUTHORIZATION', '').scan(/Bearer (.*)$/).flatten.last
272
+
273
+ unless auth_token
274
+ render json: { error: I18n.t('anoubis.errors.access_not_allowed') }
275
+ return
276
+ end
277
+
278
+ begin
279
+ data = JSON.parse(redis.get("#{redis_prefix}token:#{auth_token}"), { symbolize_names: true })
280
+ rescue StandardError
281
+ data = nil
282
+ end
283
+
284
+ if data.class == Hash
285
+ data = nil unless data.key? :uuid
286
+ else
287
+ data = nil
288
+ end
289
+
290
+ if data
291
+ data = load_userinfo data[:uuid]
292
+ end
293
+
294
+ unless data
295
+ render json: { error: I18n.t('anoubis.errors.access_not_allowed') }
296
+ return
297
+ end
298
+
299
+ render json: data
300
+ end
301
+
302
+ ##
303
+ # Load userinfo information from model and convert it into hash
304
+ # @param uuid [String] - User identifier
305
+ # @return [Hash] - User information
306
+ def load_userinfo(uuid)
307
+ data = user_model.where(uuid: uuid).first
308
+
309
+ return nil unless data
310
+
311
+ {
312
+ public: data.public,
313
+ email: data.email,
314
+ name: data.name,
315
+ surname: data.surname,
316
+ timezone: data.timezone,
317
+ locale: data.locale
318
+ }
319
+ end
320
+
321
+ ##
322
+ # Check basic oauth parameters (client_id, redirect_uri)
323
+ def check_basic_parameters
324
+ return I18n.t('anoubis.errors.is_not_defined', title: 'client_id') unless params.key? :client_id
325
+
326
+ @current_system = self.get_current_system params[:client_id]
327
+
328
+ return I18n.t('anoubis.errors.is_not_correct', title: 'client_id') unless current_system
329
+
330
+ return I18n.t('anoubis.errors.is_not_defined', title: 'redirect_uri') unless params.key? :redirect_uri
331
+
332
+ return I18n.t('anoubis.errors.is_not_correct', title: 'redirect_uri') unless current_system.request_uri.include? params[:redirect_uri]
333
+
334
+ nil
335
+ end
336
+
337
+ ##
338
+ # Check if page should be redirected to url
339
+ # @param error [String] Error message
340
+ # @param sign [String] Redirect url sign (? or &)
341
+ # @return [Boolean] return 'true' if page should be redirected
342
+ def redirect_to_uri(error, sign)
343
+ if params[:prompt] == 'none'
344
+ redirect_to params[:redirect_uri] + sign + 'error=' + ERB::Util.url_encode(error), { allow_other_host: true }
345
+ return true
346
+ end
347
+
348
+ false
349
+ end
350
+
351
+ ##
352
+ # Procedure generates keys according by used systems. Data is loaded from {AnoubisSsoServer::System}.
353
+ # @return [Hash] Hash ow JWK keys
354
+ def generate_jwks
355
+ result = {
356
+ keys: []
357
+ }
358
+
359
+ AnoubisSsoServer::System.where(state: 'opened').each do |sys|
360
+ key = {
361
+ use: 'sig',
362
+ kty: sys.jwk[:kty],
363
+ kid: "public:#{sys.uuid}",
364
+ alg: 'RS256',
365
+ n: sys.jwk[:n],
366
+ e: sys.jwk[:e]
367
+ }
368
+ result[:keys].push key
369
+ end
370
+
371
+ result
372
+ end
373
+ 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,11 @@
1
+ en:
2
+ anoubis:
3
+ errors:
4
+ system_not_defined: "SSO system is not defined in Rails.configuration.anoubis_sso_system"
5
+ is_not_defined: "%{title} isn't defined"
6
+ is_not_correct: "%{title} isn't correct"
7
+ less_than: "%{title} length should be %{size} or more symbols"
8
+ login_required: "Login required"
9
+ fields:
10
+ login: "Login not defined"
11
+ password: "Password not defined"
@@ -0,0 +1,11 @@
1
+ ru:
2
+ anoubis:
3
+ errors:
4
+ system_not_defined: "SSO система не определена в Rails.configuration.anoubis_sso_system"
5
+ is_not_defined: "Переменная %{title} не определена"
6
+ is_not_correct: "Переменная %{title} некорректна"
7
+ less_than: "Длина переменной %{title} должна быть %{size} или более символов"
8
+ login_required: "Требуется логин"
9
+ fields:
10
+ login: "Логин не задан"
11
+ password: "Пароль не задан"
data/config/routes.rb ADDED
@@ -0,0 +1,24 @@
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: 'anoubis_sso_server/main#auth', as: 'api_internal_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 'userinfo', to: 'anoubis_sso_server/open_id#userinfo', as: 'userinfo'
18
+ get 'oauth2/auth', to: 'anoubis_sso_server/open_id#auth', as: 'oauth_auth'
19
+ post 'oauth2/token', to: 'anoubis_sso_server/open_id#access_token', as: 'oauth_token'
20
+ options 'oauth2/token', to: 'anoubis_sso_server/application#options', as: nil
21
+ get 'oauth2/logout', to: 'anoubis_sso_server/open_id#logout', as: 'oauth_logout'
22
+ end
23
+ end
24
+ 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.5"
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