client_manager 0.1.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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +128 -0
  3. data/Rakefile +37 -0
  4. data/app/assets/config/client_manager_manifest.js +2 -0
  5. data/app/assets/javascripts/client_manager/application.js +16 -0
  6. data/app/assets/javascripts/client_manager/clipboard.min.js +7 -0
  7. data/app/assets/javascripts/client_manager/oneui.min.js +5 -0
  8. data/app/assets/javascripts/client_manager/passwords.js +2 -0
  9. data/app/assets/javascripts/client_manager/sessions.js +2 -0
  10. data/app/assets/javascripts/client_manager/users.js +2 -0
  11. data/app/assets/stylesheets/client_manager/application.css +17 -0
  12. data/app/assets/stylesheets/client_manager/bootstrap.min.css +5 -0
  13. data/app/assets/stylesheets/client_manager/custom.css +23 -0
  14. data/app/assets/stylesheets/client_manager/oneui.min.css +4 -0
  15. data/app/assets/stylesheets/client_manager/passwords.css +4 -0
  16. data/app/assets/stylesheets/client_manager/sessions.css +4 -0
  17. data/app/assets/stylesheets/client_manager/users.css +4 -0
  18. data/app/controllers/client_manager/application_controller.rb +31 -0
  19. data/app/controllers/client_manager/clients_controller.rb +46 -0
  20. data/app/controllers/client_manager/concerns/set_client_by_token.rb +40 -0
  21. data/app/controllers/client_manager/passwords_controller.rb +26 -0
  22. data/app/controllers/client_manager/sessions_controller.rb +38 -0
  23. data/app/controllers/client_manager/users_controller.rb +121 -0
  24. data/app/helpers/client_manager/application_helper.rb +5 -0
  25. data/app/helpers/client_manager/passwords_helper.rb +4 -0
  26. data/app/helpers/client_manager/sessions_helper.rb +4 -0
  27. data/app/helpers/client_manager/users_helper.rb +4 -0
  28. data/app/jobs/client_manager/application_job.rb +4 -0
  29. data/app/mailers/client_manager/application_mailer.rb +6 -0
  30. data/app/mailers/client_manager/registration_mailer.rb +12 -0
  31. data/app/models/client_manager/application_record.rb +5 -0
  32. data/app/models/client_manager/client.rb +33 -0
  33. data/app/models/client_manager/user.rb +42 -0
  34. data/app/views/client_manager/clients/index.html.erb +37 -0
  35. data/app/views/client_manager/clients/new.html.erb +27 -0
  36. data/app/views/client_manager/clients/show.html.erb +41 -0
  37. data/app/views/client_manager/passwords/change.html.erb +46 -0
  38. data/app/views/client_manager/registration_mailer/registration_email.html.erb +90 -0
  39. data/app/views/client_manager/sessions/login.html.erb +53 -0
  40. data/app/views/client_manager/users/edit.html.erb +55 -0
  41. data/app/views/client_manager/users/index.html.erb +53 -0
  42. data/app/views/client_manager/users/new.html.erb +39 -0
  43. data/app/views/layouts/client_manager/application.html.erb +20 -0
  44. data/app/views/layouts/client_manager/none.html.erb +1 -0
  45. data/app/views/layouts/client_manager/partials/_header.html.erb +40 -0
  46. data/app/views/layouts/client_manager/partials/_modal.html.erb +15 -0
  47. data/config/locales/devise.en.yml +62 -0
  48. data/config/routes.rb +10 -0
  49. data/db/migrate/20160816183756_create_client_manager_users.rb +11 -0
  50. data/db/migrate/20160822112804_create_client_manager_clients.rb +11 -0
  51. data/db/migrate/20160905184226_add_password_digest_to_user.rb +5 -0
  52. data/db/migrate/20160930152731_add_password_changed_flag_to_users.rb +5 -0
  53. data/db/migrate/20161007235114_add_super_admin_flag_to_users.rb +5 -0
  54. data/lib/client_manager.rb +5 -0
  55. data/lib/client_manager/engine.rb +32 -0
  56. data/lib/client_manager/version.rb +3 -0
  57. data/lib/generators/client_manager/install_generator.rb +107 -0
  58. data/lib/generators/client_manager/templates/client_manager.rb +3 -0
  59. data/lib/tasks/client_manager_tasks.rake +12 -0
  60. metadata +217 -0
@@ -0,0 +1,38 @@
1
+ require_dependency "client_manager/application_controller"
2
+
3
+ module ClientManager
4
+ class SessionsController < ApplicationController
5
+
6
+ def login
7
+ redirect_to after_login_path if client_manager_current_user
8
+ end
9
+
10
+ def logout
11
+ session.delete(:client_manager_current_user_id)
12
+ redirect_to login_path
13
+ end
14
+
15
+ def login_attempt
16
+ authorized_user = ClientManager::User.find_by(email: params[:email]).try(:authenticate, params[:password])
17
+
18
+ if !authorized_user
19
+ flash[:error] = "Invalid Email or Password"
20
+ redirect_to login_path
21
+ else
22
+ session[:client_manager_current_user_id] = authorized_user.id
23
+ redirect_to after_login_path
24
+ end
25
+
26
+ end
27
+
28
+
29
+ def after_login_path
30
+ if client_manager_current_user.superadmin
31
+ return users_path
32
+ else
33
+ return clients_path
34
+ end
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,121 @@
1
+ require_dependency "client_manager/application_controller"
2
+
3
+
4
+ module ClientManager
5
+ class UsersController < ApplicationController
6
+ before_action :set_user, only: [:destroy, :update, :edit]
7
+ before_action :authenticate_superadmin, except: [:edit, :update]
8
+ before_action :authenticate_editor, only: [:edit, :update]
9
+ before_action :set_edit_view_variables, only: [:edit]
10
+ layout "client_manager/none", only: [:new, :edit]
11
+
12
+ def new
13
+ @user = ClientManager::User.new
14
+ end
15
+
16
+ def index
17
+ @users = ClientManager::User.where.not(id: client_manager_current_user.id, superadmin: true)
18
+ end
19
+
20
+ def edit
21
+ session[:return_to] ||= request.referer
22
+ end
23
+
24
+ def update
25
+ @user.assign_attributes(user_update_params)
26
+ return if password_change_attempted? && !handle_password_update
27
+ return if max_clients_is_too_low?
28
+ if @user.save
29
+ flash[:success] = "User successfully updated"
30
+ else
31
+ flash[:error] = @user.errors.empty? ? "Error" : @user.errors.full_messages.uniq.to_sentence
32
+ end
33
+
34
+ redirect_to session.delete(:return_to)
35
+ end
36
+
37
+ def create
38
+ @user = ClientManager::User.new(user_params)
39
+ if @user.save
40
+ flash[:success] = "User successfully created"
41
+ else
42
+ flash[:error] = @user.errors.empty? ? "Error" : @user.errors.full_messages.uniq.to_sentence
43
+ end
44
+ redirect_to users_path
45
+ end
46
+
47
+
48
+ def destroy
49
+ @user.destroy
50
+ flash[:success] = "User successfully deleted"
51
+ redirect_to users_path
52
+ end
53
+
54
+
55
+ private
56
+
57
+
58
+ def authenticate_editor
59
+ if !(client_manager_current_user == @user)
60
+ authenticate_superadmin
61
+ end
62
+ end
63
+
64
+ def set_edit_view_variables
65
+ @modal_title = "MY PROFILE" if client_manager_current_user == @user
66
+ @client_manager_current_user_is_superadmin = @user.superadmin && @user == client_manager_current_user
67
+
68
+ if @client_manager_current_user_is_superadmin
69
+ @max_clients = "Infinity"
70
+ @input_type = "text"
71
+ else
72
+ @max_clients = @user.maximum_number_of_clients
73
+ @input_type = "number"
74
+ end
75
+
76
+ if client_manager_current_user == @user
77
+ @button_text = "Save Profile"
78
+ else
79
+ @button_text = "Save User"
80
+ end
81
+ end
82
+
83
+ def max_clients_is_too_low?
84
+ if !(params[:user][:maximum_number_of_clients].blank?) && (params[:user][:maximum_number_of_clients].to_i <= @user.clients.count)
85
+ flash[:error] = "User already has #{@user.clients.count} clients. Max. number of clients cannot be lower."
86
+ redirect_to session.delete(:return_to)
87
+ return true
88
+ end
89
+ return false
90
+ end
91
+
92
+ def handle_password_update
93
+ if @new_password != @new_password_confirmation
94
+ flash[:error] = "Confirmation password mismatch. Password not changed. Nothing was updated."
95
+ redirect_to session.delete(:return_to)
96
+ return false
97
+ else
98
+ @user.assign_attributes(password: @new_password)
99
+ flash[:success] = "Password changed successfully"
100
+ return true
101
+ end
102
+ end
103
+
104
+ def password_change_attempted?
105
+ @new_password = params[:user][:new_password]; @new_password_confirmation = params[:user][:new_password_confirmation]
106
+ return (!@new_password.blank? || !@new_password_confirmation.blank?) && client_manager_current_user == @user
107
+ end
108
+
109
+ def set_user
110
+ @user = ClientManager::User.find(params[:id])
111
+ end
112
+
113
+ def user_params
114
+ params.require(:user).permit(:name, :email, :maximum_number_of_clients)
115
+ end
116
+
117
+ def user_update_params
118
+ params.require(:user).permit(:name, :maximum_number_of_clients)
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,5 @@
1
+ module ClientManager
2
+ module ApplicationHelper
3
+
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module ClientManager
2
+ module PasswordsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ClientManager
2
+ module SessionsHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ClientManager
2
+ module UsersHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module ClientManager
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module ClientManager
2
+ class ApplicationMailer < ::ActionMailer::Base
3
+ default from: 'client-manager@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module ClientManager
2
+ class RegistrationMailer < ApplicationMailer
3
+
4
+
5
+ def registration_email(user)
6
+ @user = user
7
+ @application_name = ::Rails.application.class.parent.name
8
+ @url = ClientManager::Engine.routes.url_helpers.login_path
9
+ mail(to: @user.email, subject: "You have been added to #{@application_name}'s ClientManager'")
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,5 @@
1
+ module ClientManager
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,33 @@
1
+ module ClientManager
2
+ class Client < ApplicationRecord
3
+ belongs_to :user
4
+ after_create :generate_token, unless: :token_exists?
5
+ validates_presence_of :name, :user_id
6
+ validate :number_of_users_clients
7
+
8
+
9
+ def token_exists?
10
+ !self.token.blank?
11
+ end
12
+
13
+
14
+ private
15
+
16
+ def generate_token
17
+ if ClientManager.token_secret.blank?
18
+ raise "config.token_secret is missing. Please set it in the client_manager.rb initializer."
19
+ end
20
+ payload = {client_id: id}
21
+ puts payload
22
+ token = JWT.encode payload, ClientManager.token_secret, 'HS256'
23
+ self.update(token: token)
24
+ end
25
+
26
+ def number_of_users_clients
27
+ if !self.user.superadmin && self.user.maximum_number_of_clients <= self.user.clients.count
28
+ self.errors[:base] << "Maximum number of clients reached. You cannot create more than #{user.maximum_number_of_clients} clients."
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ require 'securerandom'
2
+
3
+ module ClientManager
4
+ class User < ApplicationRecord
5
+ has_secure_password
6
+
7
+ default_scope { order('created_at DESC') }
8
+ validates :name, :email, presence: true
9
+ validates :email, presence: true, uniqueness: true
10
+ validates_format_of :email, with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
11
+ validates_numericality_of :maximum_number_of_clients, allow_nil: true
12
+
13
+ before_validation :set_temporary_password, unless: :password_exists?
14
+ after_create :send_registration_email
15
+ has_many :clients, dependent: :destroy
16
+
17
+ def client_count
18
+ clients.count
19
+ end
20
+
21
+ def self.create_superadmin(name, email, password)
22
+ ClientManager::User.create(name: name, email: email, password: password, superadmin: true, maximum_number_of_clients: nil, password_changed: true)
23
+ end
24
+
25
+ private
26
+
27
+ def password_exists?
28
+ !self.password_digest.blank?
29
+ end
30
+
31
+ def set_temporary_password
32
+ self.password = SecureRandom.hex(6)
33
+ end
34
+
35
+ def send_registration_email
36
+ if !self.superadmin
37
+ RegistrationMailer.registration_email(self).deliver
38
+ end
39
+ end
40
+
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ <%= render 'layouts/client_manager/partials/header', path: new_client_path, path_title: 'Add New Client' %>
2
+ <main id="main-container">
3
+ <div class="content bg-gray-lighter">
4
+ <div class="row items-push">
5
+ <div class="col-sm-7">
6
+ <h1 class="page-heading">
7
+ Clients
8
+ </h1>
9
+ </div>
10
+ </div>
11
+ </div>
12
+ <div class="content">
13
+ <div class="content-grid push-50">
14
+ <div class="row">
15
+
16
+ <% @clients.each do |client| %>
17
+ <div class="col-lg-4">
18
+ <div class="block block-link-hover3">
19
+ <div class="block-content block-content-full clearfix">
20
+ <a href="<%= client_path(client) %>" data-method="delete" data-confirm="Are you sure you want to delete this client?" class="icon-btn">
21
+ <span class="sr-only">Delete Client <%= client.name %></span>
22
+ <i class="fa fa-trash fa-2x text-danger text-muted" aria-hidden="true"></i>
23
+ </a>
24
+ <a class="icon-btn" data-toggle="modal" data-target="#modal" href="<%= client_path(client) %>">
25
+ <span class="sr-only">View Details for Client <%= client.name %></span>
26
+ <i class="fa fa-eye fa-2x text-primary text-muted" aria-hidden="true"></i>
27
+ </a>
28
+ <h2 class="h4 font-w700"><%= client.name %></h2>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ <% end %>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ </main>
37
+ </div>
@@ -0,0 +1,27 @@
1
+ <%= form_for @client, html: {class: "form-horizontal push-5-t"} do |user_form| %>
2
+ <div class="block block-themed block-transparent remove-margin-b">
3
+ <div class="block-header bg-primary-dark">
4
+ <ul class="block-options">
5
+ <li>
6
+ <button data-dismiss="modal" type="button">
7
+ <span class="sr-only">Close Modal</span>
8
+ <i class="fa fa-close" aria-hidden="true"></i>
9
+ </button>
10
+ </li>
11
+ </ul>
12
+ <h3 class="block-title">New Client</h3>
13
+ </div>
14
+ <div class="block-content">
15
+ <div class="form-group">
16
+ <%= user_form.label :name, class: "col-xs-12", required: true %>
17
+ <div class="col-xs-12">
18
+ <%= user_form.text_field :name, class: "form-control", required: true, placeholder: "Example - 'Android Staging', 'Angular Prod'" %>
19
+ </div>
20
+ </div>
21
+ <div class="modal-footer">
22
+ <button class="btn btn-sm btn-success" type="submit"><i class="fa fa-check" aria-hidden="true"></i> Save client</button>
23
+ <button class="btn btn-sm btn-danger" type="button" data-dismiss="modal">Close</button>
24
+ </div>
25
+ </div>
26
+ </div>
27
+ <% end %>
@@ -0,0 +1,41 @@
1
+ <div class="form-horizontal push-5-t">
2
+ <div class="block block-themed block-transparent remove-margin-b">
3
+ <div class="block-header bg-primary-dark">
4
+ <ul class="block-options">
5
+ <li>
6
+ <button data-dismiss="modal" type="button">
7
+ <span class="sr-only">Close Modal</span>
8
+ <i class="fa fa-close" aria-hidden="true"></i>
9
+ </button>
10
+ </li>
11
+ </ul>
12
+ <h3 class="block-title"><%= @client.name %></h3>
13
+ </div>
14
+ <div class="block-content">
15
+ <div class="form-group">
16
+ <label class="col-xs-12">Token</label>
17
+ <div class="col-xs-12">
18
+ <input class="form-control" id="token" type="text" value="<%= @client.token %>" disabled="true">
19
+ </div>
20
+ </div>
21
+ <div class="modal-footer">
22
+ <a id="copy" class="btn btn-sm btn-default" type="button" data-clipboard-text="<%= @client.token %>">
23
+ <span class="sr-only">Copy Token</span>
24
+ <i class="fa fa-clipboard" aria-hidden="true"></i>
25
+ </a>
26
+ <a class="btn btn-sm btn-danger" type="button" data-dismiss="modal">Close</a>
27
+ </div>
28
+ </div>
29
+ </div>
30
+ </div>
31
+
32
+ <script>
33
+ $(document).ready(function () {
34
+ var clipboard = new Clipboard('#copy');
35
+
36
+ clipboard.on('success', function (e) {
37
+ var token = document.getElementById('token')
38
+ token.setSelectionRange(0, token.value.length)
39
+ });
40
+ })
41
+ </script>
@@ -0,0 +1,46 @@
1
+ <!-- Login Content -->
2
+ <div class="bg-white pulldown">
3
+ <div class="content content-boxed overflow-hidden">
4
+ <div class="row">
5
+ <div class="col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4">
6
+ <div class="push-30-t push-50 animated fadeIn">
7
+ <!-- Login Title -->
8
+ <div class="text-center">
9
+ <i class="fa fa-2x fa-circle-o-notch text-primary"></i>
10
+ <h1 class="text-muted push-15-t">Hey first timer, <br> You have to change your password to proceed.</h1>
11
+ </div>
12
+ <!-- END Login Title -->
13
+
14
+ <!-- Login Form -->
15
+ <!-- jQuery Validation (.js-validation-login class is initialized in js/pages/base_pages_login.js) -->
16
+ <!-- For more examples you can check out https://github.com/jzaefferer/jquery-validation -->
17
+ <%= form_tag({action: :change_password_attempt}, class: "js-validation-login form-horizontal push-30-t") do %>
18
+ <div class="form-group">
19
+ <div class="col-xs-12">
20
+ <div class="form-material form-material-primary floating">
21
+ <%= password_field_tag(:new_password, "", class: "form-control", id: "login-password") %>
22
+ <label for="login-password">New password</label>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ <div class="form-group">
27
+ <div class="col-xs-12">
28
+ <div class="form-material form-material-primary floating">
29
+ <%= password_field_tag(:confirm_new_password, "", class: "form-control", id: "login-confirm-password") %>
30
+ <label for="login-confirm-password">Confirm new password</label>
31
+ </div>
32
+ </div>
33
+ </div>
34
+ <div class="form-group push-30-t">
35
+ <div class="col-xs-12 col-sm-6 col-sm-offset-3 col-md-4 col-md-offset-4">
36
+ <button class="btn btn-sm btn-block btn-primary" type="submit">Change</button>
37
+ </div>
38
+ </div>
39
+ <% end %>
40
+ <!-- END Login Form -->
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ </div>
46
+ <!-- END Login Content -->