evematic 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/evematic/sessions_controller.rb +21 -0
  3. data/app/controllers/evematic/sso_callbacks_controller.rb +30 -0
  4. data/app/models/evematic/access_rule.rb +17 -0
  5. data/app/models/evematic/account.rb +12 -0
  6. data/app/models/evematic/application_record.rb +2 -4
  7. data/app/models/evematic/esi/alliance.rb +25 -0
  8. data/app/models/evematic/esi/character.rb +35 -0
  9. data/app/models/evematic/esi/corporation.rb +37 -0
  10. data/app/models/evematic/identity.rb +24 -0
  11. data/config/i18n-tasks.yml +5 -4
  12. data/config/locales/en.yml +13 -0
  13. data/lib/evematic/configuration/builder.rb +74 -0
  14. data/lib/evematic/configuration.rb +20 -0
  15. data/lib/evematic/controllers/helper.rb +7 -0
  16. data/lib/evematic/controllers/helpers/authentication.rb +90 -0
  17. data/lib/evematic/controllers.rb +2 -0
  18. data/lib/evematic/engine.rb +82 -8
  19. data/lib/evematic/esi/client.rb +89 -0
  20. data/lib/evematic/esi/helper.rb +43 -0
  21. data/lib/evematic/esi.rb +2 -0
  22. data/lib/evematic/jobs/helper.rb +3 -0
  23. data/lib/evematic/models/helper.rb +31 -0
  24. data/lib/evematic/models/mixins/access_rule.rb +43 -0
  25. data/lib/evematic/models/mixins/account.rb +38 -0
  26. data/lib/evematic/models/mixins/esi/alliance.rb +34 -0
  27. data/lib/evematic/models/mixins/esi/character.rb +41 -0
  28. data/lib/evematic/models/mixins/esi/corporation.rb +41 -0
  29. data/lib/evematic/models/mixins/esi/entity.rb +55 -0
  30. data/lib/evematic/models/mixins/esi.rb +2 -0
  31. data/lib/evematic/models/mixins/identity.rb +37 -0
  32. data/lib/evematic/models/mixins.rb +2 -0
  33. data/lib/evematic/models.rb +2 -0
  34. data/lib/evematic/routes/helper.rb +5 -0
  35. data/lib/evematic/routes/router.rb +10 -0
  36. data/lib/evematic/routes/routers/authentication.rb +14 -0
  37. data/lib/evematic/routes.rb +2 -0
  38. data/lib/evematic/version.rb +1 -1
  39. data/lib/evematic/views/helper.rb +2 -0
  40. data/lib/evematic/views.rb +2 -0
  41. data/lib/evematic.rb +37 -1
  42. metadata +182 -9
  43. data/config/locales/evematic.en.yml +0 -5
  44. data/config/routes.rb +0 -2
@@ -0,0 +1,43 @@
1
+ require "evematic/esi/client"
2
+
3
+ module Evematic::ESI::Helper
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def esi_delete(*args, **kwargs)
8
+ esi_client.delete(*args, **kwargs)
9
+ end
10
+
11
+ def esi_get(*args, **kwargs)
12
+ esi_client.get(*args, **kwargs)
13
+ end
14
+
15
+ def esi_post(*args, **kwargs)
16
+ esi_client.post(*args, **kwargs)
17
+ end
18
+
19
+ def esi_put(*args, **kwargs)
20
+ esi_client.put(*args, **kwargs)
21
+ end
22
+
23
+ def esi_client
24
+ @esi_client ||= Evematic::ESI::Client.new
25
+ end
26
+ end
27
+
28
+ def esi_delete(*args, **kwargs)
29
+ self.class.esi_delete(*args, **kwargs)
30
+ end
31
+
32
+ def esi_get(*args, **kwargs)
33
+ self.class.esi_get(*args, **kwargs)
34
+ end
35
+
36
+ def esi_post(*args, **kwargs)
37
+ self.class.esi_post(*args, **kwargs)
38
+ end
39
+
40
+ def esi_put(*args, **kwargs)
41
+ self.class.esi_put(*args, **kwargs)
42
+ end
43
+ end
@@ -0,0 +1,2 @@
1
+ module Evematic::ESI
2
+ end
@@ -0,0 +1,3 @@
1
+ module Evematic::Jobs::Helper
2
+ extend ActiveSupport::Concern
3
+ end
@@ -0,0 +1,31 @@
1
+ module Evematic::Models::Helper
2
+ extend ActiveSupport::Concern
3
+
4
+ module ClassMethods
5
+ [
6
+ :access_rule, :account, :identity,
7
+ :esi_alliance, :esi_character, :esi_corporation
8
+ ].each do |name|
9
+ define_method("#{name}_class") do
10
+ Evematic.config.send(:"#{name}_class")
11
+ end
12
+
13
+ define_method("#{name}_model") do
14
+ Evematic.config.send(:"#{name}_model")
15
+ end
16
+ end
17
+ end
18
+
19
+ [
20
+ :access_rule, :account, :identity,
21
+ :esi_alliance, :esi_character, :esi_corporation
22
+ ].each do |name|
23
+ define_method("#{name}_class") do
24
+ self.class.send(:"#{name}_class")
25
+ end
26
+
27
+ define_method("#{name}_model") do
28
+ self.class.send(:"#{name}_model")
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,43 @@
1
+ module Evematic::Models::Mixins::AccessRule
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ self.table_name = Evematic.config.access_rules_table
6
+
7
+ belongs_to :principal, polymorphic: true
8
+
9
+ validates :action, inclusion: {in: ["allow", "deny"]}
10
+ validates :principal_type,
11
+ inclusion: {
12
+ in: [
13
+ esi_alliance_class,
14
+ esi_character_class,
15
+ esi_corporation_class
16
+ ]
17
+ }
18
+
19
+ scope :allowed, -> { where(action: "allow") }
20
+ scope :denied, -> { where(action: "deny") }
21
+ end
22
+
23
+ module ClassMethods
24
+ def allows?(character)
25
+ return false if denied.exists? { |rule| rule.matches?(character) }
26
+
27
+ return true if allowed.exists? { |rule| rule.matches?(character) }
28
+
29
+ false
30
+ end
31
+ end
32
+
33
+ def matches?(character)
34
+ case principal_type
35
+ when esi_character_class
36
+ principal_id == character.id
37
+ when esi_corporation_class
38
+ principal_id == character.corporation_id
39
+ when esi_alliance_class
40
+ principal_id == character.alliance_id
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ module Evematic::Models::Mixins::Account
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ self.table_name = Evematic.config.accounts_table
6
+
7
+ has_one :main_identity,
8
+ -> { where(main: true) },
9
+ class_name: identity_class
10
+
11
+ has_one :main_character,
12
+ class_name: esi_character_class,
13
+ through: :main_identity,
14
+ source: :character
15
+ has_one :main_alliance,
16
+ class_name: esi_alliance_class,
17
+ through: :main_character,
18
+ source: :alliance
19
+ has_one :main_corporation,
20
+ class_name: esi_corporation_class,
21
+ through: :main_character,
22
+ source: :corporation
23
+
24
+ has_many :identities, dependent: :destroy
25
+
26
+ has_many :characters,
27
+ class_name: esi_character_class,
28
+ through: :identities
29
+ has_many :alliances,
30
+ class_name: esi_alliance_class,
31
+ through: :characters
32
+ has_many :corporations,
33
+ class_name: esi_corporation_class,
34
+ through: :characters
35
+
36
+ delegate :name, to: :main_character
37
+ end
38
+ end
@@ -0,0 +1,34 @@
1
+ module Evematic::Models::Mixins::ESI::Alliance
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include Evematic::Models::Mixins::ESI::Entity
6
+
7
+ self.table_name = Evematic.config.esi_alliances_table
8
+
9
+ has_one :access_rule,
10
+ as: :principal,
11
+ dependent: :restrict_with_exception
12
+
13
+ has_many :corporations,
14
+ class_name: esi_corporation_class,
15
+ dependent: :restrict_with_exception
16
+ has_many :characters,
17
+ class_name: esi_character_class,
18
+ dependent: :restrict_with_exception
19
+
20
+ has_many :identities,
21
+ class_name: identity_class,
22
+ through: :characters
23
+
24
+ has_many :accounts,
25
+ class_name: account_class,
26
+ through: :identities
27
+ end
28
+
29
+ private
30
+
31
+ def fetch_esi_attributes
32
+ esi_get("/latest/alliances/#{id}/")
33
+ end
34
+ end
@@ -0,0 +1,41 @@
1
+ module Evematic::Models::Mixins::ESI::Character
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include Evematic::Models::Mixins::ESI::Entity
6
+
7
+ self.table_name = Evematic.config.esi_characters_table
8
+
9
+ belongs_to :alliance, class_name: esi_alliance_class, optional: true
10
+ belongs_to :corporation, class_name: esi_corporation_class
11
+
12
+ has_one :access_rule,
13
+ as: :principal,
14
+ dependent: :restrict_with_exception
15
+
16
+ has_one :identity,
17
+ class_name: identity_class,
18
+ dependent: :restrict_with_exception
19
+
20
+ has_one :account,
21
+ class_name: account_class,
22
+ through: :identity
23
+
24
+ before_validation :ensure_alliance
25
+ before_validation :ensure_corporation
26
+ end
27
+
28
+ private
29
+
30
+ def fetch_esi_attributes
31
+ esi_get("/latest/characters/#{id}/")
32
+ end
33
+
34
+ def ensure_alliance
35
+ self.alliance = esi_alliance_model.find_or_create_by(id: alliance_id) if alliance_id.present?
36
+ end
37
+
38
+ def ensure_corporation
39
+ self.corporation = esi_corporation_model.find_or_create_by(id: corporation_id)
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ module Evematic::Models::Mixins::ESI::Corporation
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include Evematic::Models::Mixins::ESI::Entity
6
+
7
+ self.table_name = Evematic.config.esi_corporations_table
8
+
9
+ belongs_to :alliance,
10
+ class_name: esi_alliance_class,
11
+ optional: true
12
+
13
+ has_one :access_rule,
14
+ as: :principal,
15
+ dependent: :restrict_with_exception
16
+
17
+ has_many :characters,
18
+ class_name: esi_character_class,
19
+ dependent: :restrict_with_exception
20
+
21
+ has_many :identities,
22
+ class_name: identity_class,
23
+ through: :characters
24
+
25
+ has_many :accounts,
26
+ class_name: account_class,
27
+ through: :identities
28
+
29
+ before_validation :ensure_alliance
30
+ end
31
+
32
+ private
33
+
34
+ def fetch_esi_attributes
35
+ esi_get("/latest/corporations/#{id}/")
36
+ end
37
+
38
+ def ensure_alliance
39
+ self.alliance = esi_alliance_model.find_or_create_by(id: alliance_id) if alliance_id.present?
40
+ end
41
+ end
@@ -0,0 +1,55 @@
1
+ module Evematic::Models::Mixins::ESI::Entity
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ include Evematic::ESI::Helper
6
+
7
+ before_validation :assign_attributes_from_esi
8
+
9
+ define_model_callbacks :esi_sync
10
+ end
11
+
12
+ def assign_attributes_from_esi
13
+ return if fresh?
14
+
15
+ res = fetch_esi_attributes
16
+
17
+ attributes = parse_json(res.body).merge(
18
+ esi_etag: res.headers["etag"].delete('"'),
19
+ esi_expires_at: DateTime.parse(res.headers["expires"]),
20
+ esi_last_modified_at: DateTime.parse(res.headers["last-modified"]).change(usec: 0)
21
+ )
22
+ transform_esi_attributes(attributes) if respond_to?(:transform_esi_attributes)
23
+ assign_attributes(attributes)
24
+ end
25
+
26
+ def fresh?
27
+ return false unless persisted?
28
+
29
+ return false if esi_expires_at.blank?
30
+
31
+ esi_expires_at >= Time.zone.now
32
+ end
33
+
34
+ def sync
35
+ run_callbacks :esi_sync do
36
+ assign_attributes_from_esi
37
+ changed? ? save : true
38
+ end
39
+ end
40
+
41
+ def sync!
42
+ run_callbacks :esi_sync do
43
+ assign_attributes_from_esi
44
+ changed? ? save! : true
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def parse_json(str)
51
+ return Oj.load(str) if defined?(Oj)
52
+
53
+ JSON.parse(str)
54
+ end
55
+ end
@@ -0,0 +1,2 @@
1
+ module Evematic::Models::Mixins::ESI
2
+ end
@@ -0,0 +1,37 @@
1
+ module Evematic::Models::Mixins::Identity
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ self.table_name = Evematic.config.identities_table
6
+
7
+ belongs_to :account, class_name: account_class
8
+ belongs_to :character, class_name: esi_character_class
9
+
10
+ has_one :alliance,
11
+ class_name: esi_alliance_class,
12
+ through: :character
13
+ has_one :corporation,
14
+ class_name: esi_corporation_class,
15
+ through: :character
16
+
17
+ delegate :name, to: :character
18
+ end
19
+
20
+ module ClassMethods
21
+ def sso_authenticate_by(character)
22
+ character.sync
23
+ character.alliance.sync if character.alliance.present?
24
+ character.corporation.sync
25
+
26
+ return unless access_rule_model.allows?(character)
27
+
28
+ identity = identity_model.find_by(character:)
29
+ return identity if identity.present?
30
+
31
+ identity = identity_model.new(character:, main: true)
32
+ identity.build_account
33
+ identity.save!
34
+ identity
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,2 @@
1
+ module Evematic::Models::Mixins
2
+ end
@@ -0,0 +1,2 @@
1
+ module Evematic::Models
2
+ end
@@ -0,0 +1,5 @@
1
+ module Evematic::Routes::Helper
2
+ def evematic_authentication(**opts)
3
+ Evematic::Routes::Routers::Authentication.new(self, **opts).draw!
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require "dry-initializer"
2
+
3
+ class Evematic::Routes::Router
4
+ extend Dry::Initializer
5
+
6
+ param :routes
7
+ option :controllers
8
+ option :path_names
9
+ option :scope, default: -> { "/" }
10
+ end
@@ -0,0 +1,14 @@
1
+ class Evematic::Routes::Routers::Authentication < Evematic::Routes::Router
2
+ option :controllers, default: -> { {sessions: "evematic/sessions", sso_callbacks: "evematic/sso_callbacks"} }
3
+ option :path_names, default: -> { {login: "login", logout: "logout", callback: "auth/eve/callback"} }
4
+
5
+ def draw!
6
+ return unless Evematic.config.authentication_enabled
7
+
8
+ routes.scope(scope) do
9
+ routes.get(path_names[:callback], to: "#{controllers[:sso_callbacks]}#eve", as: :sso_callback)
10
+ routes.get(path_names[:login], to: "#{controllers[:sessions]}#new")
11
+ routes.delete(path_names[:logout], to: "#{controllers[:sessions]}#destroy")
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,2 @@
1
+ module Evematic::Routes
2
+ end
@@ -1,3 +1,3 @@
1
1
  module Evematic
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,2 @@
1
+ module Evematic::Views::Helper
2
+ end
@@ -0,0 +1,2 @@
1
+ module Evematic::Views
2
+ end
data/lib/evematic.rb CHANGED
@@ -1,6 +1,42 @@
1
1
  require "evematic/version"
2
+ require "evematic/configuration"
2
3
  require "evematic/engine"
3
4
 
4
5
  module Evematic
5
- # Your code goes here...
6
+ def self.setup
7
+ configuration_builder.clear_cache!
8
+
9
+ if defined?(Config)
10
+ settings = Config.const_name.safe_constantize
11
+
12
+ configure do |config|
13
+ config.esi_client_id = settings.esi.client_id
14
+ config.esi_client_secret = settings.esi.client_secret
15
+ end
16
+ end
17
+ end
18
+
19
+ module ESI
20
+ end
21
+
22
+ module Models
23
+ module ESI
24
+ end
25
+ end
6
26
  end
27
+
28
+ require "zeitwerk"
29
+
30
+ loader = Zeitwerk::Loader.for_gem
31
+ loader.inflector.inflect(
32
+ "api" => "API",
33
+ "eve" => "EVE",
34
+ "esi" => "ESI",
35
+ "oauth" => "OAuth",
36
+ "omniauth" => "OmniAuth",
37
+ "sso" => "SSO"
38
+ )
39
+ loader.ignore("#{__dir__}/generators")
40
+ loader.enable_reloading
41
+ loader.setup
42
+ loader.eager_load