infinum_id 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +40 -0
  5. data/.travis.yml +7 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +262 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +43 -0
  11. data/Rakefile +21 -0
  12. data/app/controllers/api/base_controller.rb +6 -0
  13. data/app/controllers/api/webhooks_controller.rb +42 -0
  14. data/app/controllers/infinum_id/application_controller.rb +6 -0
  15. data/app/controllers/infinum_id/users/omniauth_callbacks_controller.rb +30 -0
  16. data/app/controllers/infinum_id/users_controller.rb +9 -0
  17. data/app/helpers/infinum_id/path_helper.rb +7 -0
  18. data/app/mailers/application_mailer.rb +4 -0
  19. data/app/mailers/user_mailer.rb +20 -0
  20. data/app/models/infinum_id/application_record.rb +5 -0
  21. data/app/services/infinum_id/after_user_update.rb +5 -0
  22. data/app/services/infinum_id/requests/invite_sender.rb +90 -0
  23. data/app/services/infinum_id/users/finder.rb +9 -0
  24. data/app/services/infinum_id/users/invite.rb +29 -0
  25. data/app/services/infinum_id/users/updater.rb +31 -0
  26. data/app/views/user_mailer/invite_email.html.erb +8 -0
  27. data/app/views/user_mailer/invite_email.text.erb +5 -0
  28. data/app/views/user_mailer/welcome_email.html.erb +8 -0
  29. data/app/views/user_mailer/welcome_email.text.erb +5 -0
  30. data/app/workers/infinum_id/users/invite_worker.rb +11 -0
  31. data/bin/console +15 -0
  32. data/bin/setup +8 -0
  33. data/config/initializers/devise.rb +22 -0
  34. data/config/initializers/sidekiq.rb +7 -0
  35. data/config/routes.rb +11 -0
  36. data/infinum_id.gemspec +56 -0
  37. data/lib/generators/infinum_id/install_generator.rb +58 -0
  38. data/lib/generators/templates/migration.rb +25 -0
  39. data/lib/generators/templates/user.rb +19 -0
  40. data/lib/infinum_id/engine.rb +4 -0
  41. data/lib/infinum_id/version.rb +3 -0
  42. data/lib/infinum_id.rb +27 -0
  43. metadata +380 -0
@@ -0,0 +1,90 @@
1
+ module InfinumId
2
+ module Requests
3
+ class InviteSender
4
+ def initialize(user_id, current_user_uid)
5
+ @user = User.find(user_id)
6
+ @current_user_uid = current_user_uid
7
+ end
8
+
9
+ def self.call(user_id, current_user_uid)
10
+ new(user_id, current_user_uid).call
11
+ end
12
+
13
+ def call
14
+ send_invite_request
15
+ handle_response
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :user, :current_user_uid, :response
21
+
22
+ def send_invite_request
23
+ @response = HTTP.headers(headers).post(invitation_url, json: invitation_params)
24
+ end
25
+
26
+ def handle_response
27
+ case response.code
28
+ when 200
29
+ update_user
30
+ send_email if Rails.configuration.infinum_id_send_email
31
+ when 400, 403, 404
32
+ return response.to_s
33
+ when 401..599
34
+ raise response.to_s
35
+ end
36
+ end
37
+
38
+ def update_user
39
+ user.update(user_params)
40
+ end
41
+
42
+ def send_email
43
+ params[:invitation_accept_path].blank? ? send_welcome_email : send_invite_email
44
+ end
45
+
46
+ def send_invite_email
47
+ UserMailer.with(
48
+ user_id: user.id, invited_by_uid: current_user_uid,
49
+ accept_path: params[:invitation_accept_path]
50
+ ).invite_email.deliver_later
51
+ end
52
+
53
+ def send_welcome_email
54
+ UserMailer.with(
55
+ user_id: user.id, invited_by_uid: current_user_uid
56
+ ).welcome_email.deliver_later
57
+ end
58
+
59
+ def params
60
+ @params ||= ActionController::Parameters.new(response.parse)
61
+ end
62
+
63
+ def user_params
64
+ params.require(:user).permit(
65
+ :uid, :email, :first_name, :last_name, :slack_username,
66
+ :time_zone, :deactivated_at, :avatar_url
67
+ )
68
+ end
69
+
70
+ def headers
71
+ {
72
+ client_id: InfinumId.client_id,
73
+ client_secret: InfinumId.client_secret
74
+ }
75
+ end
76
+
77
+ def invitation_params
78
+ {
79
+ user: user.as_json,
80
+ invited_by_uid: current_user_uid,
81
+ send_invitation: !Rails.configuration.infinum_id_send_email
82
+ }
83
+ end
84
+
85
+ def invitation_url
86
+ "#{InfinumId.url}/api/invitations"
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,9 @@
1
+ module InfinumId
2
+ module Users
3
+ class Finder
4
+ def self.from_omniauth(auth)
5
+ User.find_by(provider: auth.provider, uid: auth.uid) if defined?(User)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ module InfinumId
2
+ module Users
3
+ class Invite
4
+ def initialize(user_params, current_user)
5
+ @user_params = user_params
6
+ @current_user = current_user
7
+ end
8
+
9
+ def self.call(user_params, current_user)
10
+ new(user_params, current_user).call
11
+ end
12
+
13
+ def call
14
+ @user = User.create(@user_params)
15
+
16
+ send_invite_request if @user.errors.empty?
17
+ @user
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :current_user, :user
23
+
24
+ def send_invite_request
25
+ InfinumId::Users::InviteWorker.perform_async(@user.id, current_user.uid)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,31 @@
1
+ module InfinumId
2
+ module Users
3
+ class Updater
4
+ def initialize(params, user)
5
+ @user_params = params.slice(*user_column_names).deep_symbolize_keys
6
+ @user = user
7
+ end
8
+
9
+ def self.call(params, user)
10
+ new(params, user).call
11
+ end
12
+
13
+ def call
14
+ update_user
15
+ InfinumId::AfterUserUpdate.call(user)
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :user_params, :user
21
+
22
+ def update_user
23
+ user.update(user_params)
24
+ end
25
+
26
+ def user_column_names
27
+ User.column_names
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ <h1>Hi <%= @user.email %></h1>
2
+ <p>
3
+ <%= @invited_by.email %> invited you to <%= @service_name %>.
4
+ </p>
5
+
6
+ <p>
7
+ Click <%= link_to 'here', @accept_path %> to accept the invitation.
8
+ </p>
@@ -0,0 +1,5 @@
1
+ Hi <%= @user.email %>
2
+
3
+ <%= @invited_by.email %> invited you to <%= @service_name %>.
4
+
5
+ Click <%= link_to 'here', @accept_path %> to accept the invitation.
@@ -0,0 +1,8 @@
1
+ <h1>Hi <%= @user.email %></h1>
2
+ <p>
3
+ <%= @invited_by.email %> added you to <%= @service_name %>.
4
+ </p>
5
+
6
+ <p>
7
+ Click <%= link_to 'here', @service_url %> to go to the service.
8
+ </p>
@@ -0,0 +1,5 @@
1
+ Hi <%= @user.email %>
2
+
3
+ <%= @invited_by.email %> added you to <%= @service_name %>.
4
+
5
+ Click <%= link_to 'here', @service_url %> to go to the service.
@@ -0,0 +1,11 @@
1
+ module InfinumId
2
+ module Users
3
+ class InviteWorker
4
+ include Sidekiq::Worker
5
+
6
+ def perform(user_id, current_user_uid)
7
+ InfinumId::Requests::InviteSender.call(user_id, current_user_uid)
8
+ end
9
+ end
10
+ end
11
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require 'rails/all'
5
+ require "infinum_id"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ require "pry"
12
+ Pry.start
13
+
14
+ # require "irb"
15
+ # IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,22 @@
1
+ Devise.setup do |config|
2
+ require 'devise/orm/active_record'
3
+
4
+ config.case_insensitive_keys = [:email]
5
+ config.strip_whitespace_keys = [:email]
6
+
7
+ config.skip_session_storage = [:http_auth]
8
+
9
+ config.stretches = Rails.env.test? ? 1 : 11
10
+
11
+ config.reconfirmable = true
12
+
13
+ config.expire_all_remember_me_on_sign_out = true
14
+
15
+ config.email_regexp = /\A[^@\s]+@[^@\s]+\z/
16
+
17
+ config.sign_out_via = :get
18
+
19
+ # ==> OmniAuth
20
+ config.omniauth :infinum_id, InfinumId.client_id, InfinumId.client_secret,
21
+ client_options: { site: InfinumId.url }
22
+ end
@@ -0,0 +1,7 @@
1
+ Sidekiq.configure_server do |config|
2
+ config.redis = { url: Rails.application.secrets[:redis_server_url] }
3
+ end
4
+
5
+ Sidekiq.configure_client do |config|
6
+ config.redis = { url: Rails.application.secrets[:redis_client_url] }
7
+ end
data/config/routes.rb ADDED
@@ -0,0 +1,11 @@
1
+ Rails.application.routes.draw do
2
+
3
+ get '/logout', to: 'infinum_id/users#destroy'
4
+
5
+ namespace :api do
6
+ scope '/webhooks', :controller => :webhooks do
7
+ post :update_user_callback
8
+ post :create_user_callback
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,56 @@
1
+ lib = File.expand_path('lib', __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'infinum_id/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'infinum_id'
7
+ spec.version = InfinumId::VERSION
8
+ spec.authors = ['Dajana Jeroncic']
9
+ spec.email = ['dajana.jeroncic@infinum.hr']
10
+
11
+ spec.summary = 'Write a short summary, because RubyGems requires one.'
12
+ spec.description = 'Write a longer description or delete this line.'
13
+ spec.homepage = 'https://github.com/infinum/rails-infinum-id-engine'
14
+ spec.license = 'MIT'
15
+
16
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
17
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
18
+ if spec.respond_to?(:metadata)
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+ else
21
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
22
+ 'public gem pushes.'
23
+ end
24
+
25
+ # Specify which files should be added to the gem when it is released.
26
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
27
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
28
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
29
+ end
30
+ spec.bindir = 'exe'
31
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ['lib']
33
+
34
+ spec.add_development_dependency 'bundler'
35
+ spec.add_development_dependency 'pry'
36
+ spec.add_development_dependency 'factory_bot_rails'
37
+ spec.add_development_dependency 'faker'
38
+ spec.add_development_dependency 'pry-rails'
39
+ spec.add_development_dependency 'pry-byebug'
40
+ spec.add_development_dependency 'rake', '~> 10.0'
41
+ spec.add_development_dependency 'rspec', '~> 3.0'
42
+ spec.add_development_dependency 'rspec-rails'
43
+ spec.add_development_dependency 'rails'
44
+ spec.add_development_dependency 'simplecov'
45
+ spec.add_development_dependency 'sqlite3'
46
+ spec.add_development_dependency 'webmock'
47
+
48
+ spec.add_dependency 'bundler'
49
+ spec.add_dependency 'devise'
50
+ spec.add_dependency 'http'
51
+ spec.add_dependency 'omniauth-infinum_id'
52
+ spec.add_dependency 'redis'
53
+ spec.add_dependency 'redis-namespace'
54
+ spec.add_dependency 'responders'
55
+ spec.add_dependency 'sidekiq'
56
+ end
@@ -0,0 +1,58 @@
1
+ require 'rails/generators/active_record'
2
+
3
+ module InfinumId
4
+ module Generators
5
+ class InstallGenerator < ::Rails::Generators::Base
6
+ include Rails::Generators::Migration
7
+
8
+ source_root File.expand_path('../templates', __dir__)
9
+
10
+ def inject_secrets
11
+ insert_into_file 'config/secrets.yml', secrets_template,
12
+ after: "bugsnag_api_key: <%= Figaro.env.bugsnag_api_key! %>\n"
13
+ end
14
+
15
+ def copy_migration
16
+ migration_template 'migration.rb', 'db/migrate/create_users.rb'
17
+ end
18
+
19
+ def copy_model
20
+ template 'user.rb', 'app/models/user.rb'
21
+ end
22
+
23
+ def inject_routes
24
+ insert_into_file 'config/routes.rb', routes_template,
25
+ after: "Rails.application.routes.draw do\n"
26
+ end
27
+
28
+ def self.next_migration_number(dirname)
29
+ ActiveRecord::Generators::Base.next_migration_number(dirname)
30
+ end
31
+
32
+ def migration_version
33
+ "[#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}]"
34
+ end
35
+
36
+ private
37
+
38
+ def routes_template
39
+ <<RUBY
40
+ devise_for :users, controllers: {
41
+ omniauth_callbacks: 'infinum_id/users/omniauth_callbacks'
42
+ }
43
+ RUBY
44
+ end
45
+
46
+ def secrets_template
47
+ <<RUBY
48
+ redis_server_url: <%= Figaro.env.redis_server_url %>
49
+ redis_client_url: <%= Figaro.env.redis_client_url %>
50
+ infinum_id:
51
+ client_id: <%= Figaro.env.infinum_id_client_id %>
52
+ client_secret: <%= Figaro.env.infinum_id_client_secret %>
53
+ url: <%= Figaro.env.infinum_id_client_url %>
54
+ RUBY
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,25 @@
1
+ class CreateUsers < ActiveRecord::Migration<%= migration_version %>
2
+ def change # rubocop:disable Metrics/MethodLength
3
+ create_table :users do |t|
4
+ t.string :email, null: false, default: ''
5
+
6
+ t.string :first_name
7
+ t.string :last_name
8
+
9
+ t.string :slack_username
10
+ t.string :time_zone
11
+
12
+ t.string :avatar_url
13
+
14
+ # Omniauth
15
+ t.string :provider
16
+ t.string :uid
17
+
18
+ t.datetime :deactivated_at
19
+
20
+ t.timestamps null: false
21
+ end
22
+
23
+ add_index :users, :email, unique: true
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ class User < ApplicationRecord
2
+ devise :omniauthable, omniauth_providers: [:infinum_id]
3
+
4
+ validates :email, uniqueness: true
5
+
6
+ def active_for_authentication?
7
+ super && !deactivated_at
8
+ end
9
+
10
+ def as_json
11
+ {
12
+ email: email,
13
+ first_name: first_name,
14
+ last_name: last_name,
15
+ slack_username: slack_username,
16
+ time_zone: time_zone
17
+ }
18
+ end
19
+ end
@@ -0,0 +1,4 @@
1
+ module InfinumId
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,3 @@
1
+ module InfinumId
2
+ VERSION = '1.0.0'
3
+ end
data/lib/infinum_id.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'infinum_id/version'
2
+ require 'omniauth/infinum_id'
3
+ require 'infinum_id/engine'
4
+ require 'devise'
5
+ require 'sidekiq'
6
+
7
+ module InfinumId
8
+ def self.url
9
+ dig_secret(:url)
10
+ end
11
+
12
+ def self.client_id
13
+ dig_secret(:client_id)
14
+ end
15
+
16
+ def self.client_secret
17
+ dig_secret(:client_secret)
18
+ end
19
+
20
+ def self.dig_secret(key)
21
+ if Rails::VERSION::MAJOR >= 5
22
+ Rails.application.secrets.dig(:infinum_id, key)
23
+ else
24
+ Rails.application.secrets.dig(:infinum_id, key.to_s)
25
+ end
26
+ end
27
+ end