aki-operations 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +87 -0
  4. data/Rakefile +27 -0
  5. data/app/controllers/admin/operations/actions_controller.rb +28 -0
  6. data/app/controllers/admin/operations/users_controller.rb +28 -0
  7. data/app/controllers/admin/operations_controller.rb +12 -0
  8. data/app/controllers/operations/enforced_controller.rb +45 -0
  9. data/app/helpers/operations_helper.rb +15 -0
  10. data/app/views/admin/operations/actions/index.html.erb +20 -0
  11. data/app/views/admin/operations/actions/index/_edit.html.erb +0 -0
  12. data/app/views/admin/operations/actions/index/_view.html.erb +156 -0
  13. data/app/views/admin/operations/index.html.erb +88 -0
  14. data/app/views/admin/operations/users/index.html.erb +20 -0
  15. data/app/views/admin/operations/users/index/_edit.html.erb +4 -0
  16. data/app/views/admin/operations/users/index/_view.html.erb +158 -0
  17. data/config/routes.rb +13 -0
  18. data/lib/generators/operations_generator.rb +10 -0
  19. data/lib/generators/templates/operations.rb +33 -0
  20. data/lib/operations.rb +152 -0
  21. data/lib/operations/act_as_operationable.rb +26 -0
  22. data/lib/operations/config.rb +64 -0
  23. data/lib/operations/core_ext.rb +28 -0
  24. data/lib/operations/enforcer.rb +71 -0
  25. data/lib/operations/engine.rb +5 -0
  26. data/lib/operations/errors.rb +6 -0
  27. data/lib/operations/errors/base_exception.rb +12 -0
  28. data/lib/operations/errors/invalid_field_error.rb +9 -0
  29. data/lib/operations/errors/invalid_operation_error.rb +26 -0
  30. data/lib/operations/errors/not_authorized_error.rb +11 -0
  31. data/lib/operations/errors/not_implemented_error.rb +9 -0
  32. data/lib/operations/errors/not_logged_in_error.rb +10 -0
  33. data/lib/operations/operation.rb +167 -0
  34. data/lib/operations/railtie.rb +17 -0
  35. data/lib/operations/utils.rb +10 -0
  36. data/lib/operations/version.rb +3 -0
  37. data/lib/tasks/operations_tasks.rake +6 -0
  38. metadata +252 -0
@@ -0,0 +1,88 @@
1
+ <div class="container">
2
+ <div class="padded">
3
+ <div class="justify-content-between d-flex w-100">
4
+ <h1>Operations Management</h1>
5
+ <h6>You are a: <%= user_role_badge(current_user) %></h6>
6
+ </div>
7
+ <div class="text-center">
8
+ <div class="small text-muted">
9
+ Operations count: <%= @operations.size %>
10
+ </div>
11
+ <div class="small text-muted">
12
+ User permissions count: <%= @permissions.size %>
13
+ </div>
14
+ </div>
15
+ </div>
16
+
17
+ <p class="very lighly padded">
18
+ You view can find below a new way of managing your operations for this application! This
19
+ will be most useful for determining what types of users are allowed to do. Adding exceptions
20
+ is also be a possibility. Each section will features on how to use them.
21
+ </p>
22
+
23
+ <div class="row">
24
+ <div class="col-md-6">
25
+ <div class="list-group">
26
+ <a href="<%= admin_operations_users_path %>" class="list-group-item list-group-item-action flex-column align-items-start">
27
+ <div class="d-flex w-100 justify-content-between">
28
+ <h3 class="mb-1">
29
+ View User permissions
30
+ </h3>
31
+ <h1 class=""><span style="vertical-align: top;" class="material-icons md-36">supervised_user_circle</span> </h1>
32
+ </div>
33
+ <p>
34
+ View all users permissions types defined on the system.
35
+ </p>
36
+ </a>
37
+ </div>
38
+ </div>
39
+ <div class="col-md-6">
40
+ <div class="list-group">
41
+ <a href="<%= admin_operations_actions_path %>" class="list-group-item list-group-item-action flex-column align-items-start">
42
+ <div class="d-flex w-100 justify-content-between">
43
+ <h3 class="mb-1">
44
+ View Actions
45
+ </h3>
46
+ <h1 class=""><span style="vertical-align: top;" class="material-icons md-36">offline_bolt</span> </h1>
47
+ </div>
48
+ <p>
49
+ View all users permissions types defined on the system.
50
+ </p>
51
+ </a>
52
+ </div>
53
+ </div>
54
+ </div>
55
+ <!--
56
+ <div style="margin-top: 15px;" class="row">
57
+ <div class="col-md-6">
58
+ <div class="list-group">
59
+ <a href="<%= admin_operations_users_path(params: {edit: true}) %>" class="list-group-item list-group-item-action flex-column align-items-start">
60
+ <div class="d-flex w-100 justify-content-between">
61
+ <h3 class="mb-1">
62
+ Edit User permissions
63
+ </h3>
64
+ <h1 class=""><span style="vertical-align: top;" class="material-icons md-36">supervised_user_circle</span> </h1>
65
+ </div>
66
+ <p>
67
+ View all users permissions types defined on the system.
68
+ </p>
69
+ </a>
70
+ </div>
71
+ </div>
72
+ <div class="col-md-6">
73
+ <div class="list-group">
74
+ <a href="<%= admin_operations_actions_path(params: {edit: true}) %>" class="list-group-item list-group-item-action flex-column align-items-start">
75
+ <div class="d-flex w-100 justify-content-between">
76
+ <h3 class="mb-1">
77
+ Edit Actions
78
+ </h3>
79
+ <h1 class=""><span style="vertical-align: top;" class="material-icons md-36">offline_bolt</span> </h1>
80
+ </div>
81
+ <p>
82
+ View all users permissions types defined on the system.
83
+ </p>
84
+ </a>
85
+ </div>
86
+ </div>
87
+ </div> -->
88
+ </div>
@@ -0,0 +1,20 @@
1
+ <div class="container">
2
+ <div class="padded">
3
+ <div class="justify-content-between d-flex w-100">
4
+ <h1><%= @editing_title %></h1>
5
+ <h6>You are a: <%= user_role_badge(current_user) %></h6>
6
+ </div>
7
+ <div class="text-center">
8
+ <div class="small text-muted">
9
+ User permissions count: <%= @permissions.size %>
10
+ </div>
11
+ </div>
12
+ </div>
13
+
14
+ <% if @editing %>
15
+ <%= render 'admin/operations/users/index/edit' %>
16
+ <% else %>
17
+ <%= render 'admin/operations/users/index/view' %>
18
+ <% end %>
19
+
20
+ </div>
@@ -0,0 +1,4 @@
1
+ <p class="very lighly padded">
2
+ Manage here all user roles that are defined on the system. You can
3
+ add, remove or edit these roles on this page.
4
+ </p>
@@ -0,0 +1,158 @@
1
+ <p>
2
+ View here all user roles that are defined on the system. <!-- Should you need
3
+ adding, removing or editing these roles, please click
4
+ <%= link_to('here', admin_operations_users_path(params: {edit: true})) %>. -->
5
+ </p>
6
+
7
+ <div class="row">
8
+ <div class="col-md-6">
9
+ <div class="very lightly padded text-center">
10
+ <b class="text-muted">User roles</b>
11
+ </div>
12
+ <div class="list-group" style="overflow-y: auto; height: 62vh;">
13
+ <% @permissions.each do |permission| %>
14
+ <div permission="<%= permission[:name] %>" class="list-group-item list-group-item-action <%= permission[:value].nil? ? "disabled" : 'clickable' %>">
15
+ <% if permission[:description] && permission[:name] %>
16
+ <div class="d-flex w-100 justify-content-between">
17
+ <%= permission[:description] %>
18
+ <small class="text-muted"><%= permission[:name] %></small>
19
+ </div>
20
+ <% else %>
21
+ <%= permission[:description] || permission[:name] %>
22
+ <% end %>
23
+ </div>
24
+ <% end %>
25
+ </div>
26
+ </div>
27
+ <div class="col-md-6">
28
+ <div class="very lightly padded">
29
+ <div class="text-center">
30
+ <b class="text-muted">More details</b>
31
+ </div>
32
+ <div style="margin-top: 10px;">
33
+ <div class="cards">
34
+ <div class="text-center">
35
+ <div before-getting="description" class="card-body">
36
+ You can click on one of the roles on the right. Doing that
37
+ will display more details about the role right here.
38
+ </div>
39
+ <div before-getting="loading" class="card-body">
40
+ Please wait while we're fetching the latest information for
41
+ this operation...
42
+ </div>
43
+ </div>
44
+ <div after-getting="description" class="card-bodys">
45
+ <p class="very lightly padded">
46
+ <div class="d-flex w-100 justify-content-between">
47
+ <span><b>Name: </b> <span after-getting="field" data-field="name"></span></span>
48
+ <div id="operation-id">
49
+ <b>Operation ID: </b> <span after-getting="field" data-field="operation_id"></span>
50
+ </div>
51
+ </div>
52
+ </p>
53
+ <div class="small">
54
+ <p class="very lightly padded">
55
+ Required category to execute this action: <b><span after-getting="field" data-field="scope"></span></b>
56
+ </p>
57
+ <div id="operations-list-cont">
58
+ <p class="very lightly padded">
59
+ The following <b><span after-getting="field" data-field="operations_count"></span></b> are associated to execute this action:
60
+ </p>
61
+ <div id="operations-list" class="list-group"></div>
62
+ </div>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+
71
+ <script type="text/javascript">
72
+ $(document).ready(function() {
73
+ $('[before-getting]').hide();
74
+ $('[after-getting]').hide();
75
+ $('[before-getting="description"]').show();
76
+ $('#operations-list-cont').hide();
77
+
78
+ $('[permission]').on('click', function(e) {
79
+ $('[permission]').removeClass('active');
80
+ $('[after-getting]').show();
81
+ $('[before-getting]').hide();
82
+ $('[before-getting="loading"]').show();
83
+
84
+ let target = $(this);
85
+ $('[permission]').addClass('disabled');
86
+ let value = target.attr('permission');
87
+ if (value) {
88
+ $.ajax({
89
+ url: '/admin/operations/users/' + value + '/details',
90
+ success: function(response) {
91
+ $('[before-getting]').hide();
92
+ $('#before-getting').hide();
93
+ $('[permission]').removeClass('disabled');
94
+ if (response.success) {
95
+ target.addClass('active');
96
+ let operation = response.operation;
97
+ let operations = response.operations;
98
+
99
+ $('[after-getting="field"][data-field="name"]')
100
+ .text(operation.name);
101
+ $('[after-getting="field"][data-field="scope"]')
102
+ .text(operation.description);
103
+ if (operation.value >= 0) {
104
+ $('#operation-id').show();
105
+ } else {
106
+ $('#operation-id').hide();
107
+ }
108
+ $('[after-getting="field"][data-field="operation_id"]')
109
+ .text(operation.value);
110
+
111
+ const operations_count = count => {
112
+ return operations.length == 1 ?
113
+ 'one operation' :
114
+ operations.length + " operations";
115
+ }
116
+ $('#operations-list').text('');
117
+ if (operations.length > 0) {
118
+ $('#operations-list-cont').show();
119
+ $('[after-getting="field"][data-field="operations_count"]')
120
+ .text(operations_count(operations.length));
121
+ let sousa_html = [];
122
+ const users_description = description => {
123
+ if (!description || !description.trim()) {
124
+ return "No description is available."
125
+ }
126
+ return description;
127
+ }
128
+
129
+ // sousa = 操作 (means "operation"). Didn't want to override the
130
+ // "operation" var already defined. And mostly to be original...かな〜
131
+ [].forEach.call(operations, function(sousa) {
132
+ sousa_html += [
133
+ '<div class="list-group">',
134
+ '<div class="list-group-item">',
135
+ '<div class="d-flex w-100 justify-content-between">',
136
+ '<span class="bold">' + sousa.name + '</span>',
137
+ '<small class="text-muted">' + sousa.scope + '</small>',
138
+ '</div>',
139
+ '<p class="mb-1">',
140
+ users_description(sousa.description),
141
+ '</p>',
142
+ '</div>',
143
+ '</div>'
144
+ ].join('');
145
+ });
146
+ $('#operations-list').html(sousa_html);
147
+ } else {
148
+ operations_list_text = "No operations can have been associated."
149
+ $('#operations-list-cont').hide();
150
+ $('[after-getting="field"][data-field="operations_count"]').text('');
151
+ }
152
+ }
153
+ }
154
+ });
155
+ }
156
+ });
157
+ });
158
+ </script>
@@ -0,0 +1,13 @@
1
+ Rails.application.routes.draw do
2
+ namespace :admin do
3
+ resources :operations, only: [ :index ]
4
+ namespace :operations do
5
+ resources :users, param: :value, only: [ :index, :create, :update, :delete ] do
6
+ get :details, on: :member
7
+ end
8
+ resources :actions, param: :uuid, only: [ :index, :create, :update, :delete ] do
9
+ get :details, on: :member
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'rails/generators/base'
2
+
3
+ class OperationsGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+ desc "Creates a Operations initializer to your application."
6
+
7
+ def copy_initializer
8
+ template 'operations.rb', 'config/initializers/operations.rb'
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ Operations::Config.setup do |config|
2
+
3
+ # Add here which operations to restrict on the app.
4
+ config.operations_list += []
5
+
6
+ # Add here what types of users will be able to execute anything
7
+ # on your app. A user type not defined won't even be able to load
8
+ # the application, so please be careful.
9
+ # config.user_roles += []
10
+
11
+ # Define what format should operations take.
12
+ config.operation_name_regex = %r{\w}
13
+
14
+ # Defines which hashing algorithm should be used for computing the
15
+ # operation's UUID. Must be a callable Class instance
16
+ config.operation_uuid_algorithm = OpenSSL::Digest::SHA256
17
+
18
+ # Enforcing the operations to your app can be done by extending the
19
+ # ApplicationController by Operations::EnforcedController. You also
20
+ # need to define which routes are associated with which operation.
21
+ #
22
+ # Caution: Any route that is not defined here will be ignored and thus
23
+ # allowed. To ensure you're not forgetting anything, you can use the "*"
24
+ # as a wildcard symbol.
25
+ #
26
+ # Example: associating all actions in the UsersController to the
27
+ # users_action operation can be done with:
28
+ # config.enforcements << {controller: :user, action: '*', operation: :users_action}
29
+ config.enforcements = [
30
+ # {controller: :application, action: '*', operation: :your_operation}
31
+ ]
32
+
33
+ end
@@ -0,0 +1,152 @@
1
+ require "operations/railtie"
2
+ require "operations/utils"
3
+ require "operations/core_ext"
4
+ require "operations/engine"
5
+ require "operations/config"
6
+ require "operations/enforcer"
7
+ require "operations/act_as_operationable"
8
+ require "operations/errors"
9
+ require "operations/operation"
10
+ require "operations/version"
11
+
12
+ module Operations
13
+ class << self
14
+ def operations_list
15
+ Operations::Config.get_operations_list
16
+ end
17
+
18
+ def user_roles
19
+ Operations::Config.user_roles
20
+ end
21
+
22
+ def named_user_roles(all: false)
23
+ roles = Operations::Config.user_roles
24
+ roles = roles.reject{|role| role[:db_value].nil?} unless all
25
+ roles.map{|role| role[:name]}
26
+ end
27
+
28
+ def operations_list_by_uuid
29
+ operations_list.map{|op| {operation: op, uuid: op.uuid}}
30
+ end
31
+
32
+ # Returns the operation associated with the passed in UUID
33
+ def from_uuid(uuid)
34
+ operations_list.select{|operation| operation.uuid == uuid}[0]
35
+ end
36
+
37
+ # Returns the operation associated with the passed in name
38
+ def from_string(name)
39
+ operations_list.select{|operation| operation.name.to_s == name.to_s}[0]
40
+ end
41
+
42
+ # Returns the operations that have scope scope
43
+ def allowing(scope)
44
+ return nil if scope.blank?
45
+ operations_list.select{|operation| operation > scope}
46
+ end
47
+
48
+ def allowed_named_roles_for(scope)
49
+ current_value = user_role_value_from(scope)
50
+ user_roles
51
+ .select{|role| role[:name] == :all ||
52
+ !role[:value].nil? && role[:value] <= current_value}
53
+ .map{|role| role[:name]}
54
+ end
55
+
56
+ def from_user_type(scope)
57
+ value = allowed_named_roles_for(scope.to_sym)
58
+ scopes = value.reject{|role| role.nil?}
59
+ end
60
+
61
+ def allowed_int_roles_for(scope)
62
+ allowed_named_roles_for(scope)
63
+ .map{|named_scope| user_role_int_from(named_scope)}
64
+ end
65
+
66
+ def allowed_value_roles_for(scope)
67
+ allowed_named_roles_for(scope)
68
+ .map{|named_scope| user_role_value_from(named_scope)}
69
+ end
70
+
71
+ # Determines if the user is allowed to execute the operation name
72
+ def allows?(user, name)
73
+ operation = from_string(name)
74
+ return false if operation.nil?
75
+ operation.users.map{|u| u.id}.include?(user.id)
76
+ end
77
+
78
+ # Returns a list of user roles names straight from the config file
79
+ def user_roles_names
80
+ user_roles.map{|ur| ur[:name]}
81
+ end
82
+
83
+ # Returns a list of users roles that qualify as role_name
84
+ def user_roles_from(role_name, select_only: false)
85
+ value = user_role_value_from(role_name)
86
+ return [] if value.nil?
87
+ compare =-> (role, value) do
88
+ select_only ? role[:value] == value : role[:value] <= value
89
+ end
90
+ user_roles
91
+ .select{|role| role[:value] && compare.call(role, value)}
92
+ .map{|role| role[:db_value]}
93
+ .reject{|res| res.nil?}
94
+ end
95
+
96
+ # Returns a list of users roles that do not qualify as role_name
97
+ def user_roles_until(role_name)
98
+ value = user_role_value_from(role_name)
99
+ return [] if value.nil?
100
+ user_roles
101
+ .select{|role| role[:value] && role[:value] > value}
102
+ .map{|role| role[:db_value]}
103
+ end
104
+
105
+ def scopes_as(scope)
106
+ value = user_role_value_from(scope)
107
+ return [] if value.nil?
108
+ return [scope] if %w{all nobody}.include?(scope.to_s)
109
+ user_roles
110
+ .select{|role| role[:value] && role[:value] >= value}
111
+ .map{|role| role[:name]}
112
+ end
113
+
114
+ def operations_for(scope)
115
+ scopes = scopes_as(scope)
116
+ list = operations_list
117
+ if %w{all nobody}.include?(scope.to_s)
118
+ list.select{|op| op == scope}.uniq
119
+ else
120
+ list.select{|op| op >= scope}.uniq
121
+ end
122
+ end
123
+
124
+ # Returns a list of users that have roles user_roles_from
125
+ def users_acting_as(role_name, select_only: false)
126
+ User.where(role: user_roles_from(role_name, select_only: select_only))
127
+ end
128
+
129
+ # Returns a list of users that have roles user_roles_until
130
+ def users_denied_from(role_name)
131
+ User.where(role: user_roles_until(role_name))
132
+ end
133
+
134
+ def user_role_int_from(role_name)
135
+ result = user_roles.select{|role| role[:name] == role_name}[0]
136
+ return nil if result.nil?
137
+ result[:db_value]
138
+ end
139
+
140
+ def user_role_value_from(role_name)
141
+ result = user_roles.select{|role| role[:name] == role_name}[0]
142
+ return nil if result.nil?
143
+ result[:value]
144
+ end
145
+
146
+ def user_role_name_from(role_int)
147
+ result = user_roles.select{|role| role[:db_value] == role_int}[0]
148
+ return nil if result.nil?
149
+ result[:name]
150
+ end
151
+ end
152
+ end