cnfs-iam 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +75 -0
- data/Rakefile +24 -0
- data/app/controllers/concerns/is_tenant_scoped.rb +21 -0
- data/app/controllers/credentials_controller.rb +23 -0
- data/app/controllers/groups_controller.rb +4 -0
- data/app/controllers/iam/application_controller.rb +6 -0
- data/app/controllers/iam/confirmations_controller.rb +51 -0
- data/app/controllers/iam/passwords_controller.rb +56 -0
- data/app/controllers/iam/sessions_controller.rb +21 -0
- data/app/controllers/policies_controller.rb +4 -0
- data/app/controllers/public_keys_controller.rb +4 -0
- data/app/controllers/roots/sessions_controller.rb +16 -0
- data/app/controllers/roots_controller.rb +4 -0
- data/app/controllers/users/confirmations_controller.rb +21 -0
- data/app/controllers/users/passwords_controller.rb +25 -0
- data/app/controllers/users/sessions_controller.rb +19 -0
- data/app/controllers/users_controller.rb +17 -0
- data/app/mailers/account_mailer.rb +74 -0
- data/app/models/action.rb +6 -0
- data/app/models/credential.rb +47 -0
- data/app/models/group.rb +15 -0
- data/app/models/group_policy_join.rb +25 -0
- data/app/models/iam/application_record.rb +7 -0
- data/app/models/policy.rb +10 -0
- data/app/models/policy_action.rb +6 -0
- data/app/models/public_key.rb +17 -0
- data/app/models/role.rb +11 -0
- data/app/models/role_policy_join.rb +6 -0
- data/app/models/root.rb +26 -0
- data/app/models/root_credential.rb +7 -0
- data/app/models/tenant.rb +68 -0
- data/app/models/user.rb +69 -0
- data/app/models/user_credential.rb +8 -0
- data/app/models/user_group.rb +25 -0
- data/app/models/user_policy_join.rb +21 -0
- data/app/models/user_role.rb +6 -0
- data/app/operations/blackcomb_user_create.rb +49 -0
- data/app/operations/user_create.rb +53 -0
- data/app/policies/action_policy.rb +3 -0
- data/app/policies/credential_policy.rb +3 -0
- data/app/policies/group_policy.rb +3 -0
- data/app/policies/iam/application_policy.rb +6 -0
- data/app/policies/policy_policy.rb +3 -0
- data/app/policies/public_key_policy.rb +4 -0
- data/app/policies/root_policy.rb +3 -0
- data/app/policies/tenant_policy.rb +5 -0
- data/app/policies/user_policy.rb +33 -0
- data/app/resources/action_resource.rb +16 -0
- data/app/resources/credential_resource.rb +13 -0
- data/app/resources/group_resource.rb +8 -0
- data/app/resources/iam/application_resource.rb +7 -0
- data/app/resources/policy_resource.rb +9 -0
- data/app/resources/public_key_resource.rb +6 -0
- data/app/resources/root_resource.rb +14 -0
- data/app/resources/tenant_resource.rb +21 -0
- data/app/resources/user_resource.rb +25 -0
- data/app/views/layouts/mailer.html.erb +4 -0
- data/app/views/user_mailer/confirmation_instructions.html.erb +5 -0
- data/app/views/user_mailer/email_changed.html.erb +7 -0
- data/app/views/user_mailer/password_change.html.erb +3 -0
- data/app/views/user_mailer/reset_password_instructions.html.erb +106 -0
- data/app/views/user_mailer/team_welcome.html.erb +107 -0
- data/app/views/user_mailer/unlock_instructions.html.erb +7 -0
- data/config/environment.rb +0 -0
- data/config/initializers/devise.rb +311 -0
- data/config/locales/devise.en.yml +65 -0
- data/config/routes.rb +17 -0
- data/config/sidekiq.yml +5 -0
- data/config/spring.rb +3 -0
- data/db/migrate/20190101000001_create_policies.rb +11 -0
- data/db/migrate/20190101000002_create_actions.rb +13 -0
- data/db/migrate/20190101000003_create_policy_actions.rb +13 -0
- data/db/migrate/20190215214352_create_roots.rb +43 -0
- data/db/migrate/20190215214353_update_tenants.rb +10 -0
- data/db/migrate/20190215214355_create_credentials.rb +14 -0
- data/db/migrate/20190215214407_create_users.rb +50 -0
- data/db/migrate/20190215214409_create_user_credentials.rb +12 -0
- data/db/migrate/20190215214410_create_user_policy_joins.rb +12 -0
- data/db/migrate/20190215214411_create_groups.rb +11 -0
- data/db/migrate/20190215214412_create_user_groups.rb +12 -0
- data/db/migrate/20190215214413_create_group_policy_joins.rb +12 -0
- data/db/migrate/20190215214415_create_roles.rb +11 -0
- data/db/migrate/20190215214416_create_user_roles.rb +12 -0
- data/db/migrate/20190215214421_create_role_policy_joins.rb +12 -0
- data/db/migrate/20190924091536_add_display_properties_to_tenants.rb +5 -0
- data/db/migrate/20191021220135_create_public_keys.rb +10 -0
- data/db/migrate/20191120083154_add_confirmable_email_to_user.rb +9 -0
- data/db/seeds/development/tenants.seeds.rb +41 -0
- data/db/seeds/development/users.seeds.rb +67 -0
- data/lib/ros/api_token_strategy.rb +24 -0
- data/lib/ros/iam.rb +18 -0
- data/lib/ros/iam/console.rb +13 -0
- data/lib/ros/iam/engine.rb +51 -0
- data/lib/ros/iam/version.rb +7 -0
- data/lib/tasks/ros/iam_tasks.rake +51 -0
- metadata +209 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UserPolicyJoin < ApplicationRecord
|
4
|
+
belongs_to :user
|
5
|
+
belongs_to :policy
|
6
|
+
|
7
|
+
after_create :add_policy_to_user
|
8
|
+
after_destroy :remove_policy_from_user
|
9
|
+
|
10
|
+
def add_policy_to_user
|
11
|
+
user.attached_policies[policy.name] ||= 0
|
12
|
+
user.attached_policies[policy.name] += 1
|
13
|
+
user.save
|
14
|
+
end
|
15
|
+
|
16
|
+
def remove_policy_from_user
|
17
|
+
user.attached_policies[policy.name] -= 1
|
18
|
+
user.attached_policies.delete(policy.name) if user.attached_policies[policy.name].zero?
|
19
|
+
user.save
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
class BlackcombUserCreate < Ros::ActivityBase
|
6
|
+
step :valdidate_root_owner
|
7
|
+
failed :invalid_user, Output(:success) => End(:failure)
|
8
|
+
step :switch_tenant
|
9
|
+
failed :invalid_schema, Output(:success) => End(:failure)
|
10
|
+
step :create_or_find_blackcomb_user
|
11
|
+
failed :failed_to_create_user
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def valdidate_root_owner(_ctx, params:, **)
|
16
|
+
Apartment::Tenant.current == 'public' && params[:current_user].root?
|
17
|
+
end
|
18
|
+
|
19
|
+
def invalid_user(_ctx, errors:, **)
|
20
|
+
errors.add(:user, 'Not a owner root')
|
21
|
+
end
|
22
|
+
|
23
|
+
def switch_tenant(_ctx, params:, **)
|
24
|
+
tenant = Tenant.find_by_schema_or_alias(params[:account_id])
|
25
|
+
return false unless tenant
|
26
|
+
|
27
|
+
tenant.switch!
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def invalid_schema(_ctx, errors:, **)
|
32
|
+
errors.add(:account_id, 'Invalid account id')
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_or_find_blackcomb_user(ctx, **)
|
36
|
+
ctx[:model] = User.find_or_initialize_by(username: 'blackcomb')
|
37
|
+
|
38
|
+
return true if ctx[:model].persisted?
|
39
|
+
|
40
|
+
password = SecureRandom.hex
|
41
|
+
ctx[:model].update(password: password, password_confirmation: password,
|
42
|
+
confirmed_at: Time.zone.today, attached_policies: { AdministratorAccess: 1 })
|
43
|
+
ctx[:model].save
|
44
|
+
end
|
45
|
+
|
46
|
+
def failed_to_create_user(ctx, model:, **)
|
47
|
+
ctx[:errors] = model.errors
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UserCreate < Ros::ActivityBase
|
4
|
+
step :check_permission
|
5
|
+
failed :not_permitted, Output(:success) => End(:failure)
|
6
|
+
step :init
|
7
|
+
step :initialize_user
|
8
|
+
step :skip_confirmation_notification
|
9
|
+
step :generate_reset_passowrd_token
|
10
|
+
step :save__model, Output(:failure) => End(:failure)
|
11
|
+
step :create_relationships
|
12
|
+
step :send_welcome_email
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def check_permission(_ctx, user:, **)
|
17
|
+
UserPolicy.new(user, User.new).create?
|
18
|
+
end
|
19
|
+
|
20
|
+
def not_permitted(_ctx, errors:, **)
|
21
|
+
errors.add(:user, 'not permitted to create a user')
|
22
|
+
end
|
23
|
+
|
24
|
+
def init(ctx, params:, **)
|
25
|
+
ctx[:relationships] = params.delete(:relationships)
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def initialize_user(ctx, params:, **)
|
30
|
+
ctx[:model] = User.new(params)
|
31
|
+
end
|
32
|
+
|
33
|
+
def skip_confirmation_notification(_ctx, model:, **)
|
34
|
+
model.skip_confirmation_notification!
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate_reset_passowrd_token(ctx, model:, **)
|
38
|
+
ctx[:reset_password_token], enc = Devise.token_generator.generate(model.class, :reset_password_token)
|
39
|
+
|
40
|
+
model.reset_password_token = enc
|
41
|
+
model.reset_password_sent_at = Time.now.utc
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_relationships(_ctx, model:, relationships:, **)
|
45
|
+
return true if relationships&.dig(:groups, :data).blank?
|
46
|
+
|
47
|
+
model.groups << Group.where(id: relationships[:groups][:data].pluck(:id)).all
|
48
|
+
end
|
49
|
+
|
50
|
+
def send_welcome_email(_ctx, model:, reset_password_token:, **)
|
51
|
+
AccountMailer.team_welcome(model, reset_password_token).deliver_later
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UserPolicy < Iam::ApplicationPolicy
|
4
|
+
# def index?
|
5
|
+
# user.action_permitted?(requested_action(__method__))
|
6
|
+
# # user.admin? || !record.published?
|
7
|
+
# end
|
8
|
+
|
9
|
+
# def self.zactions
|
10
|
+
# {
|
11
|
+
# Write: [
|
12
|
+
# 'AddUserToGroup'
|
13
|
+
# ]
|
14
|
+
# }
|
15
|
+
# end
|
16
|
+
|
17
|
+
# def update?
|
18
|
+
# super
|
19
|
+
# # does current_user have permission 'UpdateUser'
|
20
|
+
# end
|
21
|
+
|
22
|
+
# NOTE: Iam is a class in the api-client as are all namespaced models
|
23
|
+
# requested_action = Iam::Service.find_by(name: 'User').actions.find_by(name: 'DescribeUser')
|
24
|
+
# NOTE: The result should be cached as this value will not change frequently
|
25
|
+
# NOTE: There should be a way to break the cache in case the value does change
|
26
|
+
# def requested_action(method_name)
|
27
|
+
# Service.find_by(name: service_name).actions.find_by(name: action_name(method_name))
|
28
|
+
# end
|
29
|
+
|
30
|
+
# def action_name(method_name)
|
31
|
+
# method_map[method_name] + service_name
|
32
|
+
# end
|
33
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class ActionResource < Iam::ApplicationResource
|
4
|
+
# caching
|
5
|
+
attributes :name, :resource, :action_type
|
6
|
+
|
7
|
+
def action_type
|
8
|
+
@model.type
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# class ListActionResource < ActionResource; end
|
13
|
+
|
14
|
+
# class ReadActionResource < ActionResource; end
|
15
|
+
|
16
|
+
# class WriteActionResource < ActionResource; end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class CredentialResource < Iam::ApplicationResource
|
4
|
+
attributes :access_key_id, :owner_type, :owner_id, :secret_access_key
|
5
|
+
|
6
|
+
filter :access_key_id
|
7
|
+
|
8
|
+
def self.descriptions
|
9
|
+
{
|
10
|
+
access_key_id: 'The access key'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class RootResource < Iam::ApplicationResource
|
4
|
+
attributes :email, :jwt_payload
|
5
|
+
attributes :attached_policies, :attached_actions
|
6
|
+
|
7
|
+
has_many :credentials
|
8
|
+
|
9
|
+
filter :email
|
10
|
+
|
11
|
+
def attached_policies; {} end
|
12
|
+
|
13
|
+
def attached_actions; {} end
|
14
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class TenantResource < Iam::ApplicationResource
|
4
|
+
attributes :account_id, :root_id, :alias, :name, :display_properties # :locale
|
5
|
+
|
6
|
+
filter :schema_name
|
7
|
+
|
8
|
+
def self.descriptions
|
9
|
+
{
|
10
|
+
schema_name: 'The name of the <h1>Schema</h1>'
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.updatable_fields(context)
|
15
|
+
super - [:root_id]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.creatable_fields(context)
|
19
|
+
super - [:root_id]
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class UserResource < Iam::ApplicationResource
|
4
|
+
attributes :username, :api, :console, :time_zone, :properties,
|
5
|
+
:display_properties, :jwt_payload, :attached_policies,
|
6
|
+
:attached_actions, :email, :password, :password_confirmation, :unconfirmed_email
|
7
|
+
|
8
|
+
has_many :groups
|
9
|
+
has_many :credentials
|
10
|
+
has_many :public_keys
|
11
|
+
|
12
|
+
filters :username, :groups
|
13
|
+
|
14
|
+
def fetchable_fields
|
15
|
+
super - %i[password password_confirmation]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.creatable_fields(context)
|
19
|
+
super - %i[attached_policies attached_actions jwt_payload]
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.updatable_fields(context)
|
23
|
+
super - %i[attached_policies attached_actions jwt_payload]
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<p>Hello <%= @email %>!</p>
|
2
|
+
|
3
|
+
<% if @resource.try(:unconfirmed_email?) %>
|
4
|
+
<p>We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.</p>
|
5
|
+
<% else %>
|
6
|
+
<p>We're contacting you to notify you that your email has been changed to <%= @resource.email %>.</p>
|
7
|
+
<% end %>
|
@@ -0,0 +1,106 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
7
|
+
|
8
|
+
<title>PERX Reset Password</title>
|
9
|
+
|
10
|
+
<link href="https://fonts.googleapis.com/css?family=Roboto:400,500" rel="stylesheet" type="text/css">
|
11
|
+
<style type="text/css">
|
12
|
+
</style>
|
13
|
+
</head>
|
14
|
+
<body style="margin:0; padding:0; background-color:#F2F6FC;">
|
15
|
+
<center>
|
16
|
+
<table width="100%" border="0" cellpadding="0" cellspacing="0" bgcolor="#F2F6FC">
|
17
|
+
<tr>
|
18
|
+
<td align="center" height="100%" valign="top" width="100%">
|
19
|
+
<!--[if (gte mso 9)|(IE)]>
|
20
|
+
<table align="center" border="0" cellspacing="24" cellpadding="0" width="600">
|
21
|
+
<tr>
|
22
|
+
<td align="center" valign="top" width="600">
|
23
|
+
<![endif]-->
|
24
|
+
<table align="center" border="0" cellpadding="0" cellspacing="24" width="100%" style="max-width:600px;" bgcolor="#ffffff">
|
25
|
+
<tr>
|
26
|
+
<td align="left">
|
27
|
+
<img alt="Perx" src="https://cdn.uat.whistler.perxtech.io/dev1/global/assets/email/logo.png" width="96" height="34" style="width: 100%; max-width: 96px; font-family: 'Roboto', Helvetica, Arial, sans-serif; font-weight: 500; font-size: 24px; line-height: 24px; letter-spacing: 0.18px; color: #2664ed; text-transform: lowercase; display: block; border: 0px;" border="0">
|
28
|
+
</td>
|
29
|
+
</tr>
|
30
|
+
<tr>
|
31
|
+
<td align="center">
|
32
|
+
<img alt="Illustration of user unlocking account" src="https://cdn.uat.whistler.perxtech.io/dev1/global/assets/email/email-reset.png" width="268" style="width: 100%; max-width: 268px; font-family: 'Roboto', Helvetica, Arial, sans-serif; font-weight: 400; font-size: 12px; line-height: 16px; letter-spacing: 0.4px; color: #7b7b7b; display: block; border: 0px;" border="0">
|
33
|
+
</td>
|
34
|
+
</tr>
|
35
|
+
<tr>
|
36
|
+
<td align="left" valign="top" style="font-family: 'Roboto', Helvetica, Arial, sans-serif; font-weight: 400; font-size: 24px; line-height: 24px; letter-spacing: 0.18px; color: #333333;">
|
37
|
+
Hi <%= @resource.username %>,
|
38
|
+
</td>
|
39
|
+
</tr>
|
40
|
+
<tr>
|
41
|
+
<td align="left" valign="top" style="font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; letter-spacing: 0.15px; color: #7b7b7b;">
|
42
|
+
A request has been made to reset your password. Simply click on the button below to reset it.
|
43
|
+
<b style="font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; letter-spacing: 0.15px; color: #333333; font-weight: 500;">
|
44
|
+
This password reset is only valid for the next 24 hours.
|
45
|
+
</b>
|
46
|
+
</td>
|
47
|
+
</tr>
|
48
|
+
<tr>
|
49
|
+
<td align="center">
|
50
|
+
<table width="100%" border="0" cellspacing="0" cellpadding="0">
|
51
|
+
<tr>
|
52
|
+
<td align="center">
|
53
|
+
<table border="0" cellspacing="0" cellpadding="0" width="268" style="width: 100%; max-width: 268px;">
|
54
|
+
<tr>
|
55
|
+
<td align="center" width="100%" style="width: 100%; border-radius: 4px;" bgcolor="#2664ED"><a href="<%= @reset_url %>" target="_blank" style="font-size: 14px; letter-spacing: 0.75px; font-family: 'Roboto', Helvetica, Arial, sans-serif; color: #ffffff; font-weight: 500; text-decoration: none; border-radius: 4px; padding: 12px 24px; border: 1px solid #2664ED; display: block;"><!--[if mso]> <![endif]-->Reset Password<!--[if mso]> <![endif]--></a></td>
|
56
|
+
</tr>
|
57
|
+
</table>
|
58
|
+
</td>
|
59
|
+
</tr>
|
60
|
+
</table>
|
61
|
+
</td>
|
62
|
+
</tr>
|
63
|
+
<tr>
|
64
|
+
<td align="left" valign="top" style="font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; letter-spacing: 0.15px; color: #7b7b7b;">
|
65
|
+
If you did not make this request, you can safely ignore this email or
|
66
|
+
<a href="#" target="_blank" style="font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; letter-spacing: 0.15px; color: #2664ED; font-weight: 400; display: inline-block; text-decoration: none;">
|
67
|
+
contact support
|
68
|
+
</a>
|
69
|
+
if you have questions.
|
70
|
+
</td>
|
71
|
+
</tr>
|
72
|
+
<tr>
|
73
|
+
<td align="left" valign="top" style="font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 16px; line-height: 24px; letter-spacing: 0.15px; color: #7b7b7b;">
|
74
|
+
Thank you, <br />
|
75
|
+
Team Perx
|
76
|
+
</td>
|
77
|
+
</tr>
|
78
|
+
<tr>
|
79
|
+
<td height="1" style="font-size:1px; line-height:1px;"> </td>
|
80
|
+
</tr>
|
81
|
+
<tr>
|
82
|
+
<td height="1" style="font-size:1px; line-height:1px;" bgcolor="#e8e8e8"> </td>
|
83
|
+
</tr>
|
84
|
+
<tr>
|
85
|
+
<td height="1" style="font-size:1px; line-height:1px;"> </td>
|
86
|
+
</tr>
|
87
|
+
<tr>
|
88
|
+
<td align="left" valign="top" style="font-family: 'Roboto', Helvetica, Arial, sans-serif; font-size: 12px; line-height: 16px; letter-spacing: 0.4px; color: #7b7b7b;">
|
89
|
+
© 2019 Perx Technologies. All rights reserved.<br /><br />
|
90
|
+
20 Maxwell Road #02-01 <br />
|
91
|
+
Maxwell House, 069113
|
92
|
+
</td>
|
93
|
+
</tr>
|
94
|
+
|
95
|
+
</table>
|
96
|
+
<!--[if (gte mso 9)|(IE)]>
|
97
|
+
</td>
|
98
|
+
</tr>
|
99
|
+
</table>
|
100
|
+
<![endif]-->
|
101
|
+
</td>
|
102
|
+
</tr>
|
103
|
+
</table>
|
104
|
+
</center>
|
105
|
+
</body>
|
106
|
+
</html>
|