client_manager 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 -->