cnfs-iam 0.0.1.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +75 -0
  4. data/Rakefile +24 -0
  5. data/app/controllers/concerns/is_tenant_scoped.rb +21 -0
  6. data/app/controllers/credentials_controller.rb +23 -0
  7. data/app/controllers/groups_controller.rb +4 -0
  8. data/app/controllers/iam/application_controller.rb +6 -0
  9. data/app/controllers/iam/confirmations_controller.rb +51 -0
  10. data/app/controllers/iam/passwords_controller.rb +56 -0
  11. data/app/controllers/iam/sessions_controller.rb +21 -0
  12. data/app/controllers/policies_controller.rb +4 -0
  13. data/app/controllers/public_keys_controller.rb +4 -0
  14. data/app/controllers/roots/sessions_controller.rb +16 -0
  15. data/app/controllers/roots_controller.rb +4 -0
  16. data/app/controllers/users/confirmations_controller.rb +21 -0
  17. data/app/controllers/users/passwords_controller.rb +25 -0
  18. data/app/controllers/users/sessions_controller.rb +19 -0
  19. data/app/controllers/users_controller.rb +17 -0
  20. data/app/mailers/account_mailer.rb +74 -0
  21. data/app/models/action.rb +6 -0
  22. data/app/models/credential.rb +47 -0
  23. data/app/models/group.rb +15 -0
  24. data/app/models/group_policy_join.rb +25 -0
  25. data/app/models/iam/application_record.rb +7 -0
  26. data/app/models/policy.rb +10 -0
  27. data/app/models/policy_action.rb +6 -0
  28. data/app/models/public_key.rb +17 -0
  29. data/app/models/role.rb +11 -0
  30. data/app/models/role_policy_join.rb +6 -0
  31. data/app/models/root.rb +26 -0
  32. data/app/models/root_credential.rb +7 -0
  33. data/app/models/tenant.rb +68 -0
  34. data/app/models/user.rb +69 -0
  35. data/app/models/user_credential.rb +8 -0
  36. data/app/models/user_group.rb +25 -0
  37. data/app/models/user_policy_join.rb +21 -0
  38. data/app/models/user_role.rb +6 -0
  39. data/app/operations/blackcomb_user_create.rb +49 -0
  40. data/app/operations/user_create.rb +53 -0
  41. data/app/policies/action_policy.rb +3 -0
  42. data/app/policies/credential_policy.rb +3 -0
  43. data/app/policies/group_policy.rb +3 -0
  44. data/app/policies/iam/application_policy.rb +6 -0
  45. data/app/policies/policy_policy.rb +3 -0
  46. data/app/policies/public_key_policy.rb +4 -0
  47. data/app/policies/root_policy.rb +3 -0
  48. data/app/policies/tenant_policy.rb +5 -0
  49. data/app/policies/user_policy.rb +33 -0
  50. data/app/resources/action_resource.rb +16 -0
  51. data/app/resources/credential_resource.rb +13 -0
  52. data/app/resources/group_resource.rb +8 -0
  53. data/app/resources/iam/application_resource.rb +7 -0
  54. data/app/resources/policy_resource.rb +9 -0
  55. data/app/resources/public_key_resource.rb +6 -0
  56. data/app/resources/root_resource.rb +14 -0
  57. data/app/resources/tenant_resource.rb +21 -0
  58. data/app/resources/user_resource.rb +25 -0
  59. data/app/views/layouts/mailer.html.erb +4 -0
  60. data/app/views/user_mailer/confirmation_instructions.html.erb +5 -0
  61. data/app/views/user_mailer/email_changed.html.erb +7 -0
  62. data/app/views/user_mailer/password_change.html.erb +3 -0
  63. data/app/views/user_mailer/reset_password_instructions.html.erb +106 -0
  64. data/app/views/user_mailer/team_welcome.html.erb +107 -0
  65. data/app/views/user_mailer/unlock_instructions.html.erb +7 -0
  66. data/config/environment.rb +0 -0
  67. data/config/initializers/devise.rb +311 -0
  68. data/config/locales/devise.en.yml +65 -0
  69. data/config/routes.rb +17 -0
  70. data/config/sidekiq.yml +5 -0
  71. data/config/spring.rb +3 -0
  72. data/db/migrate/20190101000001_create_policies.rb +11 -0
  73. data/db/migrate/20190101000002_create_actions.rb +13 -0
  74. data/db/migrate/20190101000003_create_policy_actions.rb +13 -0
  75. data/db/migrate/20190215214352_create_roots.rb +43 -0
  76. data/db/migrate/20190215214353_update_tenants.rb +10 -0
  77. data/db/migrate/20190215214355_create_credentials.rb +14 -0
  78. data/db/migrate/20190215214407_create_users.rb +50 -0
  79. data/db/migrate/20190215214409_create_user_credentials.rb +12 -0
  80. data/db/migrate/20190215214410_create_user_policy_joins.rb +12 -0
  81. data/db/migrate/20190215214411_create_groups.rb +11 -0
  82. data/db/migrate/20190215214412_create_user_groups.rb +12 -0
  83. data/db/migrate/20190215214413_create_group_policy_joins.rb +12 -0
  84. data/db/migrate/20190215214415_create_roles.rb +11 -0
  85. data/db/migrate/20190215214416_create_user_roles.rb +12 -0
  86. data/db/migrate/20190215214421_create_role_policy_joins.rb +12 -0
  87. data/db/migrate/20190924091536_add_display_properties_to_tenants.rb +5 -0
  88. data/db/migrate/20191021220135_create_public_keys.rb +10 -0
  89. data/db/migrate/20191120083154_add_confirmable_email_to_user.rb +9 -0
  90. data/db/seeds/development/tenants.seeds.rb +41 -0
  91. data/db/seeds/development/users.seeds.rb +67 -0
  92. data/lib/ros/api_token_strategy.rb +24 -0
  93. data/lib/ros/iam.rb +18 -0
  94. data/lib/ros/iam/console.rb +13 -0
  95. data/lib/ros/iam/engine.rb +51 -0
  96. data/lib/ros/iam/version.rb +7 -0
  97. data/lib/tasks/ros/iam_tasks.rake +51 -0
  98. metadata +209 -0
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ Ros::Iam::Engine.routes.draw do
4
+ jsonapi_resources :public_keys
5
+ devise_for :users, module: 'users', defaults: { format: :json }
6
+ devise_for :roots, module: 'roots', defaults: { format: :json }
7
+
8
+ jsonapi_resources :credentials
9
+ jsonapi_resources :groups
10
+ jsonapi_resources :policies
11
+ jsonapi_resources :roots
12
+ jsonapi_resources :users
13
+
14
+ # TODO: temporary route until we support registering callbacks to allow
15
+ # a root owner account to create a tenant
16
+ post '/blackcomb_credentials', to: 'credentials#blackcomb'
17
+ end
@@ -0,0 +1,5 @@
1
+ ---
2
+ :queues:
3
+ - iam_default
4
+ - mailers
5
+
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ Spring.application_root = './spec/dummy'
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePolicies < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :policies do |t|
6
+ t.string :name, null: false, index: { unique: true }
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateActions < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :actions do |t|
6
+ t.string :name, null: false, index: { unique: true }
7
+ t.string :type, null: false
8
+ t.string :resource
9
+
10
+ t.timestamps
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreatePolicyActions < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :policy_actions do |t|
6
+ t.references :policy, foreign_key: true
7
+ t.references :action, foreign_key: true
8
+
9
+ t.timestamps
10
+ end
11
+ add_index :policy_actions, %i[policy_id action_id], unique: true
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRoots < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :roots do |t|
6
+ t.boolean :api, null: false, default: false
7
+ t.string :time_zone, null: false, default: 'UTC'
8
+
9
+ ## Database authenticatable
10
+ t.string :email, null: false, default: '', index: { unique: true }
11
+ t.string :encrypted_password, null: false, default: ''
12
+
13
+ ## Recoverable
14
+ t.string :reset_password_token, index: { unique: true }
15
+ t.datetime :reset_password_sent_at
16
+
17
+ ## Rememberable
18
+ t.datetime :remember_created_at
19
+
20
+ ## Trackable
21
+ # t.integer :sign_in_count, default: 0, null: false
22
+ # t.datetime :current_sign_in_at
23
+ # t.datetime :last_sign_in_at
24
+ # t.string :current_sign_in_ip
25
+ # t.string :last_sign_in_ip
26
+
27
+ ## Confirmable
28
+ # t.string :confirmation_token
29
+ # t.datetime :confirmed_at
30
+ # t.datetime :confirmation_sent_at
31
+ # t.string :unconfirmed_email # Only if using reconfirmable
32
+
33
+ ## Lockable
34
+ # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
35
+ # t.string :unlock_token # Only if unlock strategy is :email or :both
36
+ # t.datetime :locked_at
37
+
38
+ t.timestamps null: false
39
+ end
40
+ # add_index :users, :confirmation_token, unique: true
41
+ # add_index :users, :unlock_token, unique: true
42
+ end
43
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UpdateTenants < ActiveRecord::Migration[6.0]
4
+ def change
5
+ add_reference :tenants, :root, null: false, foreign_key: true, index: { unique: true }
6
+ add_column :tenants, :alias, :string, null: true, index: { unique: true }
7
+ add_column :tenants, :name, :string
8
+ add_column :tenants, :state, :string
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateCredentials < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :credentials do |t|
6
+ t.references :owner, polymorphic: true
7
+ t.string :access_key_id, length: 20, index: { unique: true }
8
+ t.string :secret_access_key_digest
9
+
10
+ t.timestamps
11
+ end
12
+ # add_index :credentials, :access_key_id, unique: true
13
+ end
14
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateUsers < ActiveRecord::Migration[6.0]
4
+ # rubocop:disable Metrics/MethodLength
5
+ def change
6
+ create_table :users do |t|
7
+ t.boolean :console, null: false, default: false, comment: 'Allow console access when true'
8
+ t.boolean :api, null: false, default: false, comment: 'Allow API access when true'
9
+ t.string :time_zone, null: false, default: 'UTC', comment: 'Adjust timestamps to this time zone'
10
+ t.jsonb :attached_policies, null: false, default: {}
11
+ t.jsonb :attached_actions, null: false, default: {}
12
+ t.jsonb :properties, null: false, default: {}, comment: 'Custom properties of the user'
13
+ t.jsonb :display_properties, null: false, default: {}, comment: 'Custom display properties of the user'
14
+
15
+ ## Database authenticatable
16
+ t.string :username, null: false, index: { unique: true }
17
+ t.string :encrypted_password, null: false, default: '', comment: 'Required if console is true'
18
+
19
+ ## Recoverable
20
+ t.string :reset_password_token, index: { unique: true }
21
+ t.datetime :reset_password_sent_at
22
+
23
+ ## Rememberable
24
+ t.datetime :remember_created_at
25
+
26
+ ## Trackable
27
+ # t.integer :sign_in_count, default: 0, null: false
28
+ # t.datetime :current_sign_in_at
29
+ # t.datetime :last_sign_in_at
30
+ # t.string :current_sign_in_ip
31
+ # t.string :last_sign_in_ip
32
+
33
+ ## Confirmable
34
+ # t.string :confirmation_token
35
+ # t.datetime :confirmed_at
36
+ # t.datetime :confirmation_sent_at
37
+ # t.string :unconfirmed_email # Only if using reconfirmable
38
+
39
+ ## Lockable
40
+ # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
41
+ # t.string :unlock_token # Only if unlock strategy is :email or :both
42
+ # t.datetime :locked_at
43
+
44
+ t.timestamps null: false
45
+ end
46
+ # add_index :users, :confirmation_token, unique: true
47
+ # add_index :users, :unlock_token, unique: true
48
+ end
49
+ # rubocop:enable Metrics/MethodLength
50
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateUserCredentials < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :user_credentials do |t|
6
+ t.references :user, foreign_key: true
7
+ t.integer :credential_id, foreign_key: true, index: { unique: true }
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateUserPolicyJoins < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :user_policy_joins do |t|
6
+ t.references :user, foreign_key: true
7
+ t.references :policy, foreign_key: true
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateGroups < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :groups do |t|
6
+ t.string :name
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateUserGroups < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :user_groups do |t|
6
+ t.references :user, foreign_key: true
7
+ t.references :group, foreign_key: true
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateGroupPolicyJoins < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :group_policy_joins do |t|
6
+ t.references :group, foreign_key: true
7
+ t.references :policy, foreign_key: true
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRoles < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :roles do |t|
6
+ t.string :name
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateUserRoles < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :user_roles do |t|
6
+ t.references :user, foreign_key: true
7
+ t.references :role, foreign_key: true
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class CreateRolePolicyJoins < ActiveRecord::Migration[6.0]
4
+ def change
5
+ create_table :role_policy_joins do |t|
6
+ t.references :role, foreign_key: true
7
+ t.references :policy, foreign_key: true
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ class AddDisplayPropertiesToTenants < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :tenants, :display_properties, :jsonb, default: {}
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ class CreatePublicKeys < ActiveRecord::Migration[6.0]
2
+ def change
3
+ create_table :public_keys do |t|
4
+ t.references :user
5
+ t.string :content
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class AddConfirmableEmailToUser < ActiveRecord::Migration[6.0]
2
+ def change
3
+ add_column :users, :email, :string
4
+ add_column :users, :confirmation_token, :string
5
+ add_column :users, :confirmed_at, :datetime
6
+ add_column :users, :confirmation_sent_at, :datetime
7
+ add_column :users, :unconfirmed_email, :string
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ # NOTE: Disabling event logging
4
+ logging_mem = Settings.event_logging.enabled
5
+ Settings.event_logging.enabled = false
6
+ # if 'platform owner' account does not exist then create it and initialize credentials file
7
+ initialize = Root.find_by(email: 'root@platform.com').nil?
8
+
9
+ create_list = if initialize
10
+ [
11
+ { email: 'root@platform.com', password: 'asdfjkl;', api: true, alias: 'owner' },
12
+ { email: 'root@generic.com', password: 'asdfjkl;', alias: 'generic' },
13
+ { email: 'root@banking.com', password: 'asdfjkl;', alias: 'banking' },
14
+ { email: 'root@telco.com', password: 'asdfjkl;', alias: 'telco' },
15
+ { email: 'root@insurance.com', password: 'asdfjkl;', alias: 'insurance' },
16
+ { email: 'root@retail.com', password: 'asdfjkl;', alias: 'retail' }
17
+ ]
18
+ else
19
+ # TODO: We don't need it anymore
20
+ # id = Root.last.id
21
+ # create_list = [
22
+ # { email: "root@client#{id + 1}.com", password: 'asdfjkl;' },
23
+ # { email: "root@client#{id + 2}.com", password: 'asdfjkl;' }
24
+ # ]
25
+ []
26
+ end
27
+ owner_root = Root.create!(email: 'root@owner.com', password: 'asdfjkl;')
28
+ Tenant.create(schema_name: 'public', name: 'Account 1', alias: 'root', root: owner_root)
29
+
30
+ @created_list = []
31
+ create_list.each do |account|
32
+ tenant_alias = account.delete(:alias)
33
+ Root.create!(account).tap do |root|
34
+ root.create_tenant(schema_name: Tenant.account_id_to_schema(root.id.to_s * 9)[0..10],
35
+ name: "Account #{root.id}", state: :active, alias: tenant_alias)
36
+ credential = Credential.create(owner: root)
37
+ @created_list.append(type: 'root', owner: root, tenant: root.tenant, credential: credential,
38
+ secret: credential.secret_access_key)
39
+ end
40
+ end
41
+ Settings.event_logging.enabled = logging_mem
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ # ubocop:disable Metrics/BlockLength
4
+ after 'development:tenants' do
5
+ Tenant.all.each do |tenant|
6
+ next if tenant.id.eql? 1
7
+
8
+ tenant.switch do
9
+ if Policy.count.zero?
10
+ # Add IAM Policies
11
+ Policy.create(name: 'AdministratorAccess')
12
+ Policy.create(name: 'IamUserFullAccess')
13
+ Policy.create(name: 'IamUserReadOnlyAccess')
14
+ end
15
+ next if User.count.positive?
16
+
17
+ user = User.create(username: 'Admin',
18
+ console: true,
19
+ api: true,
20
+ time_zone: 'Asia/Singapore',
21
+ email: 'admin@example.com',
22
+ confirmed_at: DateTime.now,
23
+ password: 'asdfjkl;')
24
+ User.create(username: 'Microsite',
25
+ console: false,
26
+ api: true,
27
+ time_zone: 'Asia/Singapore',
28
+ email: 'microsite@example.com',
29
+ confirmed_at: DateTime.now)
30
+ # user.locale: 'en-US'
31
+ credential = user.credentials.create
32
+ @created_list.append(type: 'user',
33
+ owner: user,
34
+ tenant: tenant,
35
+ credential: credential,
36
+ secret: credential.secret_access_key)
37
+
38
+ # Create a Group
39
+ group_admin = Group.create(name: 'Admin')
40
+
41
+ # Attach the Admin Policy to the Group
42
+ group_admin.policies << Policy.first
43
+
44
+ # Assign the first User to the Admin Group
45
+ group_admin.users << User.first
46
+
47
+ # NOTE: create remainign groups
48
+ Group.create(name: 'Creator')
49
+ Group.create(name: 'Customer Support')
50
+ Group.create(name: 'Manager')
51
+ Group.create(name: 'Viewer')
52
+
53
+ # Role.create(name: 'PerxServiceRoleForIAM')
54
+ # Role.create(name: 'PerxUserReadOnlyAccess')
55
+ end
56
+ end
57
+
58
+ # Append newly created credentials to credentials.json in the tmp directory
59
+ FileUtils.mkdir_p(Ros.host_tmp_dir)
60
+ File.open("#{Ros.host_tmp_dir}/credentials.json", 'w') do |f|
61
+ f.puts(@created_list.to_json)
62
+ end
63
+ # Output the contents so that it can be captured by the helm log file when deployed into kubernetes
64
+ STDOUT.puts 'Credentials are next:'
65
+ STDOUT.puts File.read("#{Ros.host_tmp_dir}/credentials.json")
66
+ end
67
+ # ubocop:enable Metrics/BlockLength
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ros
4
+ # NOTE: Overrides methods in ros-core gem
5
+ class ApiTokenStrategy
6
+ def authenticate_basic
7
+ credential = if access_key[:owner_type].eql?('Root')
8
+ Apartment::Tenant.switch('public') { Credential.find_by(access_key_id: access_key_id) }
9
+ else
10
+ Credential.find_by(access_key_id: access_key_id)
11
+ end
12
+ return unless credential
13
+
14
+ credential.authenticate_secret_access_key(secret_access_key).try(:owner)
15
+ end
16
+
17
+ def authenticate_bearer
18
+ return unless (urn = Urn.from_jwt(token))
19
+ return unless urn.model_name.in? %w[Root User]
20
+
21
+ urn.instance
22
+ end
23
+ end
24
+ end