virtuatable-core 1.4.0 → 1.6.0.dev1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/core/decorators/account.rb +9 -0
- data/lib/core/decorators/application.rb +17 -0
- data/lib/core/decorators/base.rb +15 -0
- data/lib/core/decorators/campaign.rb +20 -0
- data/lib/core/decorators/session.rb +13 -0
- data/lib/core/decorators/token.rb +12 -0
- data/lib/core/decorators.rb +10 -0
- data/lib/core/helpers/declarators.rb +2 -2
- data/lib/core/helpers/parameters.rb +5 -1
- data/lib/core/helpers/responses.rb +3 -13
- data/lib/core/models/oauth/access_token.rb +9 -24
- data/lib/core/models/oauth/authorization.rb +5 -1
- data/lib/core/services/accounts.rb +36 -8
- data/lib/core/services/applications.rb +43 -0
- data/lib/core/services/authorizations.rb +55 -0
- data/lib/core/services/base.rb +21 -4
- data/lib/core/services/campaigns.rb +28 -0
- data/lib/core/services/registry.rb +29 -5
- data/lib/core/services/sessions.rb +26 -10
- data/lib/core/services/tokens.rb +51 -0
- data/lib/core/services.rb +4 -0
- data/lib/core/version.rb +1 -1
- data/lib/core.rb +7 -1
- metadata +29 -5
- data/lib/core/models/oauth/refresh_token.rb +0 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d04349e236702f20e0c4a2542187eb4d1a0ef59d6ff4b44f90ff7d1a2484f783
|
4
|
+
data.tar.gz: f1e41d1f2b0a89c2851d617c2642222351a96cda77f0baeefed1a67eb98150d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1a87bca549bdc10d5f9dbee7c483051a0af0d7ec0cfb5594051a64b617e07fe8e32f8fcb7231398a078e4ca30d4d51e3434eb703bbdf5f5790474da897d8c6d
|
7
|
+
data.tar.gz: 910921c135060a8b78065c146e41ccc6e13f32e24ed75bafd4b3f507058ccb22a47c88f75ecfff991831c65382e1c56c3971124b07855e7e7513242871eec184
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Core
|
2
|
+
module Decorators
|
3
|
+
class Application < Core::Decorators::Base
|
4
|
+
def to_h
|
5
|
+
{
|
6
|
+
client_id: client_id,
|
7
|
+
name: name,
|
8
|
+
premium: premium
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_secret?(secret)
|
13
|
+
object.client_secret == secret
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Core
|
2
|
+
module Decorators
|
3
|
+
class Campaign < Draper::Decorator
|
4
|
+
delegate_all
|
5
|
+
|
6
|
+
def to_simple_h
|
7
|
+
{
|
8
|
+
id: id.to_s,
|
9
|
+
title: title,
|
10
|
+
description: description,
|
11
|
+
tags: tags,
|
12
|
+
players: {
|
13
|
+
current: invitations.where(status: :accepted).count,
|
14
|
+
max: max_players
|
15
|
+
}
|
16
|
+
}
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Core
|
2
|
+
module Decorators
|
3
|
+
autoload :Account, 'core/decorators/account'
|
4
|
+
autoload :Application, 'core/decorators/application'
|
5
|
+
autoload :Base, 'core/decorators/base'
|
6
|
+
autoload :Campaign, 'core/decorators/campaign'
|
7
|
+
autoload :Session, 'core/decorators/session'
|
8
|
+
autoload :Token, 'core/decorators/token'
|
9
|
+
end
|
10
|
+
end
|
@@ -13,9 +13,9 @@ module Core
|
|
13
13
|
# @param verb [String] the HTTP method for the route.
|
14
14
|
# @param path [String] the whole URI with parameters for the route.
|
15
15
|
# @param options [Hash] the additional options for the route.
|
16
|
-
def api_route(verb, path, premium: false, scopes: [
|
16
|
+
def api_route(verb, path, premium: false, scopes: [], &block)
|
17
17
|
send(verb, path) do
|
18
|
-
scope_objects = fetch_scopes(scopes)
|
18
|
+
scope_objects = fetch_scopes(scopes + ['data::usage'])
|
19
19
|
appli = application(premium: premium)
|
20
20
|
check_app_scopes(appli, scope_objects)
|
21
21
|
check_token_scopes(token, scope_objects)
|
@@ -12,8 +12,12 @@ module Core
|
|
12
12
|
super.merge(body_params)
|
13
13
|
end
|
14
14
|
|
15
|
+
def sym_params
|
16
|
+
params.map { |k, v| [k.to_sym, v] }.to_h
|
17
|
+
end
|
18
|
+
|
15
19
|
# The parameters from the JSON body if it is sent.
|
16
|
-
# @return [Hash] the JSON body parsed as a
|
20
|
+
# @return [Hash] the JSON body parsed as a dict ionary.
|
17
21
|
def body_params
|
18
22
|
request.body.rewind
|
19
23
|
JSON.parse(request.body.read.to_s)
|
@@ -13,7 +13,7 @@ module Core
|
|
13
13
|
def api_list(items)
|
14
14
|
halt 200, {
|
15
15
|
count: items.count,
|
16
|
-
items: items.map { |item|
|
16
|
+
items: items.map { |item| item.to_h }
|
17
17
|
}.to_json
|
18
18
|
end
|
19
19
|
|
@@ -21,13 +21,13 @@ module Core
|
|
21
21
|
# returning the informations about the created item.
|
22
22
|
# @param item [Object] any object that responds to #to_h to display to the user.
|
23
23
|
def api_created(item)
|
24
|
-
halt 201,
|
24
|
+
halt 201, item.to_json
|
25
25
|
end
|
26
26
|
|
27
27
|
# Displays an item with the standards of the API.
|
28
28
|
# @param item [Object] the item to display as a JSON formatted hash.
|
29
29
|
def api_item(item)
|
30
|
-
halt 200,
|
30
|
+
halt 200, item.to_json
|
31
31
|
end
|
32
32
|
|
33
33
|
# Displays a message with a 200 status code
|
@@ -35,16 +35,6 @@ module Core
|
|
35
35
|
def api_ok(message)
|
36
36
|
api_item message: message
|
37
37
|
end
|
38
|
-
|
39
|
-
private
|
40
|
-
|
41
|
-
def enhanced_h(item)
|
42
|
-
(item.respond_to?(:enhance) ? item.enhance : item).to_h
|
43
|
-
end
|
44
|
-
|
45
|
-
def enhanced_json(item)
|
46
|
-
enhanced_h(item).to_json
|
47
|
-
end
|
48
38
|
end
|
49
39
|
end
|
50
40
|
end
|
@@ -13,44 +13,29 @@ module Core
|
|
13
13
|
# @!attribute [rw] value
|
14
14
|
# @return [String] the value of the token, returned to the application when built.
|
15
15
|
field :value, type: String, default: ->{ SecureRandom.hex }
|
16
|
-
# @!attribute [rw] expiration
|
17
|
-
# @return [Integer] the time, in seconds, after which the token is declared expired, and thus can't be used anymore.
|
18
|
-
field :expiration, type: Integer, default: 86400
|
19
16
|
|
20
17
|
# @!attribute [rw] authorization
|
21
18
|
# @return [Core::Models::OAuth::Authorization] the authorization code that issued this token to the application for this user.
|
22
|
-
belongs_to :authorization, class_name: 'Core::Models::OAuth::Authorization', inverse_of: :tokens
|
19
|
+
belongs_to :authorization, class_name: 'Core::Models::OAuth::Authorization', inverse_of: :tokens, optional: true
|
20
|
+
# @!attribute [rw] generator
|
21
|
+
# @return [Core::Models::Oauth::AccessToken] the token that generated this one.
|
22
|
+
belongs_to :generator, class_name: 'Core::Models::OAuth::AccessToken', inverse_of: :generated, optional: true
|
23
23
|
|
24
|
-
|
25
|
-
#
|
26
|
-
|
27
|
-
# @return [Core::Models::OAuth::RefreshToken] the refresh token linked to this token
|
28
|
-
has_one :refresh_token, class_name: 'Core::Models::OAuth::RefreshToken', inverse_of: :token
|
24
|
+
# @!attribute [rw] generated
|
25
|
+
# @return [Core::Models::OAuth::AccessToken] the token that has been generated by the current one.
|
26
|
+
has_one :generated, class_name: 'Core::Models::OAuth::AccessToken', inverse_of: :generator
|
29
27
|
|
30
28
|
validates :value,
|
31
29
|
presence: {message: 'required'},
|
32
30
|
uniqueness: {message: 'uniq'}
|
33
31
|
|
34
|
-
# Checks if the current date is inferior to the creation date + expiration period
|
35
|
-
# @return [Boolean] TRUE if the token is expired, FALSE otherwise.
|
36
|
-
def expired?
|
37
|
-
# Handles the case where the token is given to a premium app (our apps have infinite tokens).
|
38
|
-
return false if premium?
|
39
|
-
return true if refresh_token.used?
|
40
|
-
|
41
|
-
created_at.to_time.to_i + expiration < Time.now.to_i
|
42
|
-
end
|
43
|
-
|
44
32
|
# Returns the scopes this access token can use to access the application
|
45
33
|
# @return [Array<Core::Models::OAuth::Scope>] the array of scopes from the linked authorization
|
46
34
|
def scopes
|
47
|
-
|
48
|
-
return Core::Models::OAuth::Scope.all.to_a if premium?
|
49
|
-
|
50
|
-
authorization.scopes
|
35
|
+
premium ? Core::Models::OAuth::Scope.all.to_a : authorization.scopes
|
51
36
|
end
|
52
37
|
|
53
|
-
def premium
|
38
|
+
def premium
|
54
39
|
authorization.application.premium
|
55
40
|
end
|
56
41
|
end
|
@@ -17,7 +17,7 @@ module Core
|
|
17
17
|
field :code, type: String, default: ->{ SecureRandom.hex }
|
18
18
|
# @!attribute [rw] expiration
|
19
19
|
# @return [Integer] the time, in seconds, after which the authorization is declared expired.
|
20
|
-
field :expiration, type: Integer, default:
|
20
|
+
field :expiration, type: Integer, default: 60
|
21
21
|
|
22
22
|
# @!attribute [rw] account
|
23
23
|
# @return [Arkaaan::Account] the account granting the authorization to access its data to the application.
|
@@ -41,6 +41,10 @@ module Core
|
|
41
41
|
def expired?
|
42
42
|
created_at.to_time.to_i + expiration < Time.now.to_i
|
43
43
|
end
|
44
|
+
|
45
|
+
def used?
|
46
|
+
tokens.count > 0
|
47
|
+
end
|
44
48
|
end
|
45
49
|
end
|
46
50
|
end
|
@@ -1,16 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Core
|
2
4
|
module Services
|
5
|
+
# Service managing user accounts.
|
6
|
+
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
3
7
|
class Accounts < Core::Services::Base
|
4
|
-
|
8
|
+
include Singleton
|
9
|
+
|
10
|
+
# Gets an account given the nickname of the user.
|
11
|
+
#
|
12
|
+
# @param username [String] the nickname the user chose at account creation.
|
13
|
+
# @return [Core::Models::Account] the account linked to this username.
|
14
|
+
#
|
15
|
+
# @raise [Core::Helpers::Errors::BadRequest] if the username is not given.
|
16
|
+
# @raise [Core::Helpers::Errors::NotFound] if the username does not exist.
|
17
|
+
def get_by_username(username: nil, **ignored)
|
18
|
+
require_parameters username: username
|
5
19
|
account = Core::Models::Account.find_by(username: username)
|
6
|
-
if account.nil?
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
20
|
+
raise unknown_err(field: 'username') if account.nil?
|
21
|
+
|
22
|
+
Core::Decorators::Account.new(account)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Gets and authenticates an account using its credentials.
|
26
|
+
#
|
27
|
+
# @param username [String] the nickname the user chose at account creation.
|
28
|
+
# @param password [String] the password, in clear, to identify the user with.
|
29
|
+
# @return [Core::Decorators::Account] the account if it is correctly found.
|
30
|
+
#
|
31
|
+
# @raise [Core::Helpers::Errors::BadRequest] if a needed parameter is not given.
|
32
|
+
# @raise [Core::Helpers::Errors::NotFound] if a user with this nickname is not found.
|
33
|
+
# @raise [Core::Helpers::Errors::Forbidden] if the password does not match the user.
|
34
|
+
def get_by_credentials(username: nil, password: nil, **ignored)
|
35
|
+
require_parameters password: password
|
36
|
+
account = get_by_username(username: username)
|
37
|
+
|
38
|
+
raise forbidden_err(field: 'password', error: 'wrong') unless account.has_password?(password)
|
39
|
+
|
12
40
|
account
|
13
41
|
end
|
14
42
|
end
|
15
43
|
end
|
16
|
-
end
|
44
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Core
|
4
|
+
module Services
|
5
|
+
# Service managing applications, allowing easy access to them with or without
|
6
|
+
# providing client secret for example.
|
7
|
+
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
8
|
+
class Applications < Core::Services::Base
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
# Gets an application given its credentials (client UUID and client secret).
|
12
|
+
#
|
13
|
+
# @param client_id [String] the unique public identifier of the application.
|
14
|
+
# @param client_secret [String] the password for the application.
|
15
|
+
# @return [Core::Models::OAuth::Application] the application if it has been found.
|
16
|
+
#
|
17
|
+
# @raise [Core::Helpers::Errors::BadRequest] if a parameter is not correctly given.
|
18
|
+
# @raise [Core::Helpers::Errors::Forbidden] if the client secret is wrong for this application.
|
19
|
+
# @raise [Core::Helpers::Errors::Unknown] if the application is not found.
|
20
|
+
def get_by_credentials(client_id: nil, client_secret: nil, **_ignored)
|
21
|
+
require_parameters client_secret: client_secret
|
22
|
+
application = get_by_id(client_id: client_id)
|
23
|
+
raise forbidden_err(field: 'client_secret', error: 'wrong') unless application.has_secret?(client_secret)
|
24
|
+
|
25
|
+
application
|
26
|
+
end
|
27
|
+
|
28
|
+
# Gets an application given its client UUID.
|
29
|
+
#
|
30
|
+
# @param client_id [String] the unique identifier of the application to get
|
31
|
+
# @return [Core::Models::OAuth::Application] the application found if no error is raised.
|
32
|
+
#
|
33
|
+
# @raise [Core::Helpers::Errors::Unknown] if the application is not found.
|
34
|
+
def get_by_id(client_id: nil, **_ignored)
|
35
|
+
require_parameters client_id: client_id
|
36
|
+
application = Core::Models::OAuth::Application.find_by(client_id: client_id)
|
37
|
+
raise unknown_err(field: 'client_id') if application.nil?
|
38
|
+
|
39
|
+
Core::Decorators::Application.new(application)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Core
|
4
|
+
module Services
|
5
|
+
# Service managing authorization codes. These codes represent the access a user
|
6
|
+
# is giving to an application on all or part of its data.
|
7
|
+
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
8
|
+
class Authorizations < Core::Services::Base
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
# Gets the authorization code corresponding to the provided value if it is linked to the
|
12
|
+
# application matching the provided credentials. Otherwise it raises errors.
|
13
|
+
#
|
14
|
+
# @param client_id [String] the UUID of the application.
|
15
|
+
# @param client_secret [String] the password of the application.
|
16
|
+
# @param authorization_code [String] the code of the authorization you're trying to get.
|
17
|
+
#
|
18
|
+
# @return [Core::Models::OAuth::Authorization] the authorization code object if found.
|
19
|
+
#
|
20
|
+
# @raise [Core::Helpers::Errors::NotFound] if the application or the authorization is unknown
|
21
|
+
# @raise [Core::Helpers::Errors::BadRequest] if any parameter is nil.
|
22
|
+
# @raise [Core::Helpers::Errors::Forbidden] if the secret does not match the application,
|
23
|
+
# or the authorization code does not belong to the application.
|
24
|
+
def get_by_credentials(client_id: nil, client_secret: nil, authorization_code: nil, **_ignored)
|
25
|
+
require_parameters authorization_code: authorization_code
|
26
|
+
application = Core.svc.applications.get_by_credentials(
|
27
|
+
client_id: client_id,
|
28
|
+
client_secret: client_secret
|
29
|
+
)
|
30
|
+
authorization = get_by_code(authorization_code: authorization_code)
|
31
|
+
raise mismatch_error if authorization.application.id.to_s != application.id.to_s
|
32
|
+
|
33
|
+
authorization
|
34
|
+
end
|
35
|
+
|
36
|
+
# Gets an authorization code by its corresponding value.
|
37
|
+
# @param authorization_code [String] the code value of the authorization object.
|
38
|
+
# @return [Core::Models::OAuth::Authorization] the authorization object.
|
39
|
+
# @raise [Core::Helpers::Errors::NotFound] if the authorization code is not found.
|
40
|
+
def get_by_code(authorization_code: nil, **_ignored)
|
41
|
+
require_parameters authorization_code: authorization_code
|
42
|
+
authorization = Core::Models::OAuth::Authorization.find_by(code: authorization_code)
|
43
|
+
raise unknown_err(field: 'authorization_code') if authorization.nil?
|
44
|
+
|
45
|
+
authorization
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def mismatch_error
|
51
|
+
bad_request_err(field: 'client_id', error: 'mismatch')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/core/services/base.rb
CHANGED
@@ -1,11 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Core
|
2
4
|
module Services
|
3
5
|
class Base
|
4
|
-
|
6
|
+
# Raises an error if any parameter is nil, and nothing if all parameters are found.
|
7
|
+
# @raise [Core::Helpers::Errors::BadRequest] an error if any parameter is nil.
|
8
|
+
def require_parameters(**parameters)
|
9
|
+
parameters.keys.each do |key|
|
10
|
+
value = parameters[key]
|
11
|
+
raise bad_request_err(field: key.to_s, error: 'required') if value.nil?
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def bad_request_err(field: nil, error: nil)
|
16
|
+
Core::Helpers::Errors::BadRequest.new(field: field, error: error)
|
17
|
+
end
|
18
|
+
|
19
|
+
def unknown_err(field: nil, error: 'unknown')
|
20
|
+
Core::Helpers::Errors::NotFound.new(field: field, error: error)
|
21
|
+
end
|
5
22
|
|
6
|
-
def
|
7
|
-
|
23
|
+
def forbidden_err(field: nil, error: 'forbidden')
|
24
|
+
Core::Helpers::Errors::Forbidden.new(field: field, error: error)
|
8
25
|
end
|
9
26
|
end
|
10
27
|
end
|
11
|
-
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Core
|
4
|
+
module Services
|
5
|
+
class Campaigns
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
# Lists all the campaigns of a user identified by its account.
|
9
|
+
#
|
10
|
+
# @param account [Core::Models::Account] the user requesting its campaigns.
|
11
|
+
# @param page [Integer] the page in the list of campaigns to return to the users.
|
12
|
+
# @param per_page [Integer] the number of campaigns per page.
|
13
|
+
#
|
14
|
+
# @return [Array<Hash>] an array of hash representing campaigns.
|
15
|
+
def list(account, page: 0, per_page: 20, **_ignored)
|
16
|
+
campaigns = campaigns(account).skip(page * per_page).limit(per_page)
|
17
|
+
campaigns.map do |campaign|
|
18
|
+
Core::Decorators::Campaign.new(campaign).to_simple_h
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def campaigns(account)
|
23
|
+
invitations = account.invitations.where(enum_status: 'creator')
|
24
|
+
Core::Models::Campaign.where(:id.in => invitations.map(&:campaign_id))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -1,15 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Core
|
2
4
|
module Services
|
3
5
|
# The registry holds references to all the services accessible in the library. To access
|
4
|
-
# all services and be able to manage resources easily, just instanciate the
|
6
|
+
# all services and be able to manage resources easily, just instanciate the
|
5
7
|
class Registry
|
8
|
+
include Singleton
|
6
9
|
|
7
|
-
|
10
|
+
# @!attribute [r] accounts
|
11
|
+
# @return [Core::Services::Accounts] the service managing accounts
|
12
|
+
attr_reader :accounts
|
13
|
+
# @!attribute [r] sessions
|
14
|
+
# @return [Core::Services::Sessions] the service managing sessions
|
15
|
+
attr_reader :sessions
|
16
|
+
# @!attribute [r] campaigns
|
17
|
+
# @return [Core::Services::Campaigns] the service managing campaigns
|
18
|
+
attr_reader :campaigns
|
19
|
+
# @!attribute [r] applications
|
20
|
+
# @return [Core::Services::Applications] the service managing applications
|
21
|
+
attr_reader :applications
|
22
|
+
# @!attribute [r] authorizations
|
23
|
+
# @return [Core::Services::Authorizations] the service managing authorizations
|
24
|
+
attr_reader :authorizations
|
25
|
+
# @!attribute [r] tokens
|
26
|
+
# @return [Core::Services::Tokens] the service managing OAuth access tokens
|
27
|
+
attr_reader :tokens
|
8
28
|
|
9
29
|
def initialize
|
10
|
-
@accounts = Core::Services::Accounts.
|
11
|
-
@sessions = Core::Services::Sessions.
|
30
|
+
@accounts = Core::Services::Accounts.instance
|
31
|
+
@sessions = Core::Services::Sessions.instance
|
32
|
+
@campaigns = Core::Services::Campaigns.instance
|
33
|
+
@applications = Core::Services::Applications.instance
|
34
|
+
@authorizations = Core::Services::Authorizations.instance
|
35
|
+
@tokens = Core::Services::Tokens.instance
|
12
36
|
end
|
13
37
|
end
|
14
38
|
end
|
15
|
-
end
|
39
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'bcrypt'
|
2
4
|
require 'securerandom'
|
3
5
|
|
@@ -6,6 +8,8 @@ module Core
|
|
6
8
|
# Service concerning sessions (log in and log out)
|
7
9
|
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
8
10
|
class Sessions < Core::Services::Base
|
11
|
+
include Singleton
|
12
|
+
|
9
13
|
# Creates a new session from the given user credentials. IT will
|
10
14
|
# * check that the user exists in the database
|
11
15
|
# * check that the password matches the user encrypted password
|
@@ -15,19 +19,31 @@ module Core
|
|
15
19
|
# @param username [string] the name of the user trying to log in
|
16
20
|
# @param password [string] the password the user has provided
|
17
21
|
# @return [Core::Models::Authentication::Session] the login session
|
18
|
-
def
|
19
|
-
account =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
)
|
25
|
-
end
|
26
|
-
return Core::Models::Authentication::Session.create(
|
22
|
+
def create_from_credentials(username: nil, password: nil, **ignored)
|
23
|
+
account = Core.svc.accounts.get_by_credentials(
|
24
|
+
username: username,
|
25
|
+
password: password
|
26
|
+
)
|
27
|
+
session = Core::Models::Authentication::Session.create(
|
27
28
|
account: account,
|
28
29
|
token: SecureRandom.uuid
|
29
30
|
)
|
30
31
|
end
|
32
|
+
|
33
|
+
# Gets the session by its unique identifier.
|
34
|
+
#
|
35
|
+
# @param session_id [String] the unique identifier of the session you're searching.
|
36
|
+
# @return [Core::Decorators::Session] the decorated session to display in the API.
|
37
|
+
#
|
38
|
+
# @raise [Core::Helpers::Errors::BadRequest] if the session ID is not given or nil
|
39
|
+
# @raise [Core::Helpers::Errors::NotFound] if no session with its ID exist in the database.
|
40
|
+
def get_by_id(session_id: nil, **ignored)
|
41
|
+
require_parameters session_id: session_id
|
42
|
+
session = Core::Models::Authentication::Session.find_by(token: session_id)
|
43
|
+
raise unknown_err(field: 'session_id') if session.nil?
|
44
|
+
|
45
|
+
Core::Decorators::Session.new(session)
|
46
|
+
end
|
31
47
|
end
|
32
48
|
end
|
33
|
-
end
|
49
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Core
|
4
|
+
module Services
|
5
|
+
# Service handling every operations concerning access tokens. This should mainly be
|
6
|
+
# used in the authentication backend as we should be the only ones to manage tokens.
|
7
|
+
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
8
|
+
class Tokens < Core::Services::Base
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
def create_from_authorization(client_id: nil, client_secret: nil, authorization_code: nil, **_ignored)
|
12
|
+
authorization = Core.svc.authorizations.get_by_credentials(
|
13
|
+
client_id: client_id,
|
14
|
+
client_secret: client_secret,
|
15
|
+
authorization_code: authorization_code
|
16
|
+
)
|
17
|
+
raise forbidden_err(field: 'authorization_code', error: 'used') if authorization.used?
|
18
|
+
|
19
|
+
created = Core::Models::OAuth::AccessToken.create(authorization: authorization)
|
20
|
+
Core::Decorators::Token.new(created)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Refreshes the token for the next request the client wants to issue by re-creating it
|
24
|
+
# from the previous token to add it to the tokens chain.
|
25
|
+
#
|
26
|
+
def create_from_token(client_id: nil, client_secret: nil, token: nil, **_ignored)
|
27
|
+
token = get_by_value(token: token)
|
28
|
+
authorization = Core.svc.authorizations.get_by_credentials(
|
29
|
+
client_id: client_id,
|
30
|
+
client_secret: client_secret,
|
31
|
+
authorization_code: token.authorization.code
|
32
|
+
)
|
33
|
+
raise forbidden_err(field: 'token', error: 'used') unless token.generated.nil?
|
34
|
+
|
35
|
+
created = Core::Models::OAuth::AccessToken.create(
|
36
|
+
generator: token,
|
37
|
+
authorization: authorization
|
38
|
+
)
|
39
|
+
Core::Decorators::Token.new(created)
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_by_value(token: nil, **_ignored)
|
43
|
+
require_parameters token: token
|
44
|
+
token = Core::Models::OAuth::AccessToken.find_by(value: token)
|
45
|
+
raise unknown_err(field: 'token') if token.nil?
|
46
|
+
|
47
|
+
Core::Decorators::Token.new(token)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/core/services.rb
CHANGED
@@ -5,8 +5,12 @@ module Core
|
|
5
5
|
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
6
6
|
module Services
|
7
7
|
autoload :Accounts, 'core/services/accounts'
|
8
|
+
autoload :Applications, 'core/services/applications'
|
9
|
+
autoload :Authorizations, 'core/services/authorizations'
|
8
10
|
autoload :Base, 'core/services/base'
|
11
|
+
autoload :Campaigns, 'core/services/campaigns'
|
9
12
|
autoload :Registry, 'core/services/registry'
|
10
13
|
autoload :Sessions, 'core/services/sessions'
|
14
|
+
autoload :Tokens, 'core/services/tokens'
|
11
15
|
end
|
12
16
|
end
|
data/lib/core/version.rb
CHANGED
data/lib/core.rb
CHANGED
@@ -1,12 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
%w[active_model mongoid active_support].each { |g| require g }
|
3
|
+
%w[active_model mongoid active_support draper].each { |g| require g }
|
4
4
|
|
5
5
|
# Main module of the application, holding all the subsequent classes.
|
6
6
|
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
7
7
|
module Core
|
8
8
|
autoload :Controllers, 'core/controllers'
|
9
|
+
autoload :Decorators, 'core/decorators'
|
9
10
|
autoload :Helpers, 'core/helpers'
|
10
11
|
autoload :Models, 'core/models'
|
11
12
|
autoload :Services, 'core/services'
|
13
|
+
|
14
|
+
# Returns the registry of services for easier access to each of them.
|
15
|
+
def self.svc
|
16
|
+
Core::Services::Registry.instance
|
17
|
+
end
|
12
18
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virtuatable-core
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0.dev1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vincent Courtois
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-05-
|
11
|
+
date: 2022-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: database_cleaner
|
@@ -276,6 +276,20 @@ dependencies:
|
|
276
276
|
- - '='
|
277
277
|
- !ruby/object:Gem::Version
|
278
278
|
version: 2.1.0
|
279
|
+
- !ruby/object:Gem::Dependency
|
280
|
+
name: draper
|
281
|
+
requirement: !ruby/object:Gem::Requirement
|
282
|
+
requirements:
|
283
|
+
- - ">="
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: '0'
|
286
|
+
type: :runtime
|
287
|
+
prerelease: false
|
288
|
+
version_requirements: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - ">="
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: '0'
|
279
293
|
description: This gem holds the model layer for my table-top RPG games application.
|
280
294
|
email: courtois.vincent@outlook.com
|
281
295
|
executables: []
|
@@ -285,6 +299,13 @@ files:
|
|
285
299
|
- lib/core.rb
|
286
300
|
- lib/core/controllers.rb
|
287
301
|
- lib/core/controllers/base.rb
|
302
|
+
- lib/core/decorators.rb
|
303
|
+
- lib/core/decorators/account.rb
|
304
|
+
- lib/core/decorators/application.rb
|
305
|
+
- lib/core/decorators/base.rb
|
306
|
+
- lib/core/decorators/campaign.rb
|
307
|
+
- lib/core/decorators/session.rb
|
308
|
+
- lib/core/decorators/token.rb
|
288
309
|
- lib/core/helpers.rb
|
289
310
|
- lib/core/helpers/accounts.rb
|
290
311
|
- lib/core/helpers/applications.rb
|
@@ -332,14 +353,17 @@ files:
|
|
332
353
|
- lib/core/models/oauth/access_token.rb
|
333
354
|
- lib/core/models/oauth/application.rb
|
334
355
|
- lib/core/models/oauth/authorization.rb
|
335
|
-
- lib/core/models/oauth/refresh_token.rb
|
336
356
|
- lib/core/models/oauth/scope.rb
|
337
357
|
- lib/core/models/ruleset.rb
|
338
358
|
- lib/core/services.rb
|
339
359
|
- lib/core/services/accounts.rb
|
360
|
+
- lib/core/services/applications.rb
|
361
|
+
- lib/core/services/authorizations.rb
|
340
362
|
- lib/core/services/base.rb
|
363
|
+
- lib/core/services/campaigns.rb
|
341
364
|
- lib/core/services/registry.rb
|
342
365
|
- lib/core/services/sessions.rb
|
366
|
+
- lib/core/services/tokens.rb
|
343
367
|
- lib/core/version.rb
|
344
368
|
homepage: https://rubygems.org/gems/virtuatable-core
|
345
369
|
licenses:
|
@@ -356,9 +380,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
356
380
|
version: '0'
|
357
381
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
358
382
|
requirements:
|
359
|
-
- - "
|
383
|
+
- - ">"
|
360
384
|
- !ruby/object:Gem::Version
|
361
|
-
version:
|
385
|
+
version: 1.3.1
|
362
386
|
requirements: []
|
363
387
|
rubygems_version: 3.2.3
|
364
388
|
signing_key:
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module Core
|
2
|
-
module Models
|
3
|
-
module OAuth
|
4
|
-
# A refresh token is used when an access token is expired, to get a new one. It is then recreated for the next expiration.
|
5
|
-
# @author Vincent Courtois <courtois.vincent@outlook.com>
|
6
|
-
class RefreshToken
|
7
|
-
include Mongoid::Document
|
8
|
-
include Mongoid::Timestamps
|
9
|
-
|
10
|
-
store_in collection: 'oauth_refresh_tokens'
|
11
|
-
|
12
|
-
# @!attribute [rw] value
|
13
|
-
# @return [String] the value of the token, returned to the application when built.
|
14
|
-
field :value, type: String, default: ->{ SecureRandom.hex }
|
15
|
-
# @!attribute [rw] used_at
|
16
|
-
# @return [DateTime] the date and time at which this refresh token has been useds to create a new access token.
|
17
|
-
field :used_at, type: DateTime, default: nil
|
18
|
-
|
19
|
-
# @!attribute [rw] authorization
|
20
|
-
# @return [Core::Models::OAuth::Authorization] the authorization code that issued this token to the application for this user.
|
21
|
-
belongs_to :token, class_name: 'Core::Models::OAuth::AccessToken', inverse_of: :refresh_token
|
22
|
-
|
23
|
-
def used?
|
24
|
-
!used_at.nil? && used_at < DateTime.now
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|