ucb_rails_user 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +166 -0
  4. data/Rakefile +36 -0
  5. data/app/assets/config/ucb_rails_user_manifest.js +0 -0
  6. data/app/assets/javascripts/ucb_rails_user/datatables.js +15441 -0
  7. data/app/assets/javascripts/ucb_rails_user/scripts.js +15 -0
  8. data/app/assets/javascripts/ucb_rails_user/ucb_rails_user.js +39 -0
  9. data/app/assets/stylesheets/ucb_rails_user/components/_add_users_search_results_table.sass +2 -0
  10. data/app/assets/stylesheets/ucb_rails_user/components/_loader.sass +46 -0
  11. data/app/assets/stylesheets/ucb_rails_user/components/_users_table.sass +10 -0
  12. data/app/assets/stylesheets/ucb_rails_user/datatables.css +201 -0
  13. data/app/assets/stylesheets/ucb_rails_user/main.sass +2 -0
  14. data/app/assets/stylesheets/ucb_rails_user/styles.css +19 -0
  15. data/app/controllers/ucb_rails_user/concerns/controller_methods.rb +92 -0
  16. data/app/controllers/ucb_rails_user/concerns/home_controller.rb +17 -0
  17. data/app/controllers/ucb_rails_user/concerns/sessions_controller.rb +68 -0
  18. data/app/controllers/ucb_rails_user/concerns/users_controller.rb +132 -0
  19. data/app/controllers/ucb_rails_user/home_controller.rb +10 -0
  20. data/app/controllers/ucb_rails_user/sessions_controller.rb +9 -0
  21. data/app/controllers/ucb_rails_user/users_controller.rb +8 -0
  22. data/app/helpers/ucb_rails_user/users_helper.rb +18 -0
  23. data/app/helpers/ucb_rails_user_helper.rb +17 -0
  24. data/app/models/concerns/user_concerns.rb +53 -0
  25. data/app/models/ucb_rails_user/configuration/cas.rb +53 -0
  26. data/app/models/ucb_rails_user/configuration/configuration.rb +72 -0
  27. data/app/models/ucb_rails_user/configuration/email.rb +73 -0
  28. data/app/models/ucb_rails_user/configuration/exception_notification.rb +21 -0
  29. data/app/models/ucb_rails_user/configuration/ldap.rb +50 -0
  30. data/app/models/ucb_rails_user/ldap_person/entry.rb +62 -0
  31. data/app/models/ucb_rails_user/ldap_person/finder.rb +87 -0
  32. data/app/models/ucb_rails_user/ldap_person/test_finder.rb +57 -0
  33. data/app/models/ucb_rails_user/user_ldap_service.rb +65 -0
  34. data/app/models/ucb_rails_user/user_session_manager/active_in_user_table.rb +29 -0
  35. data/app/models/ucb_rails_user/user_session_manager/admin_in_user_table.rb +13 -0
  36. data/app/models/ucb_rails_user/user_session_manager/base.rb +54 -0
  37. data/app/models/ucb_rails_user/user_session_manager/in_people_ou.rb +27 -0
  38. data/app/models/ucb_rails_user/user_session_manager/in_people_ou_add_to_users_table.rb +21 -0
  39. data/app/models/ucb_rails_user/user_session_manager/ldap_person_user_wrapper.rb +29 -0
  40. data/app/models/user.rb +8 -0
  41. data/app/views/ucb_rails_user/home/logged_in.html.haml +5 -0
  42. data/app/views/ucb_rails_user/home/not_logged_in.html.haml +5 -0
  43. data/app/views/ucb_rails_user/lps/_form.html.haml +19 -0
  44. data/app/views/ucb_rails_user/lps/_modal.html.haml +9 -0
  45. data/app/views/ucb_rails_user/lps/_results.html.haml +34 -0
  46. data/app/views/ucb_rails_user/lps/search.js.haml +3 -0
  47. data/app/views/ucb_rails_user/users/_form.html.haml +14 -0
  48. data/app/views/ucb_rails_user/users/_search_results.html.haml +24 -0
  49. data/app/views/ucb_rails_user/users/_user.html.haml +12 -0
  50. data/app/views/ucb_rails_user/users/edit.html.haml +3 -0
  51. data/app/views/ucb_rails_user/users/index.html.haml +23 -0
  52. data/app/views/ucb_rails_user/users/new.html.haml +16 -0
  53. data/app/views/ucb_rails_user/users/search.js.haml +4 -0
  54. data/config/initializers/simple_form.rb +171 -0
  55. data/config/initializers/simple_form_bootstrap.rb +154 -0
  56. data/config/locales/simple_form.en.yml +31 -0
  57. data/config/routes.rb +20 -0
  58. data/db/migrate/20170324221936_create_users.rb +29 -0
  59. data/lib/tasks/ucb_rails_user_tasks.rake +4 -0
  60. data/lib/templates/erb/scaffold/_form.html.erb +13 -0
  61. data/lib/ucb_rails_user.rb +21 -0
  62. data/lib/ucb_rails_user/engine.rb +28 -0
  63. data/lib/ucb_rails_user/version.rb +3 -0
  64. metadata +277 -0
@@ -0,0 +1,13 @@
1
+ module UcbRailsUser
2
+ module UserSessionManager
3
+ class AdminInUserTable < ActiveInUserTable
4
+
5
+ private
6
+
7
+ def user_table_entry
8
+ active_admin_user
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,54 @@
1
+ class UcbRailsUser::UserSessionManager::Base
2
+ attr_accessor :uid
3
+
4
+ def login(uid)
5
+ raise NotImplementedError
6
+ end
7
+
8
+ def current_user(uid)
9
+ raise NotImplementedError
10
+ end
11
+
12
+ def log_request(user)
13
+ end
14
+
15
+ def logout(user)
16
+ end
17
+
18
+ def people_ou_entry(uid_in=nil)
19
+ self.uid = uid_in if uid_in.present?
20
+
21
+ @people_ou_entry ||= begin
22
+ if @people_ou_entry = UcbRailsUser::LdapPerson::Finder.find_by_uid(uid)
23
+ @people_ou_entry
24
+ else
25
+ UcbRailsUser.logger.debug "#{self.class} people_ou_entry not found for uid: #{uid.inspect}"
26
+ nil
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def active_user
34
+ @active_user ||= User.active.find_by_ldap_uid(uid)
35
+ end
36
+
37
+ def active_admin_user
38
+ @active_user ||= User.active.superuser.find_by_ldap_uid(uid)
39
+ end
40
+
41
+ def ldap_person_user_wrapper(ldap_person_entry)
42
+ UcbRailsUser::UserSessionManager::LdapPersonUserWrapper.new(ldap_person_entry)
43
+ end
44
+
45
+ class << self
46
+ def current_user=(user)
47
+ Thread.current[:current_user] = user
48
+ end
49
+
50
+ def current_user
51
+ Thread.current[:current_user]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,27 @@
1
+ module UcbRailsUser
2
+ module UserSessionManager
3
+ class InPeopleOu < Base
4
+
5
+ def login(uid)
6
+ self.uid = uid
7
+
8
+ if people_ou_entry.present?
9
+ current_user(uid)
10
+ else
11
+ false
12
+ end
13
+ end
14
+
15
+ def current_user(uid)
16
+ self.uid = uid
17
+
18
+ if people_ou_entry.present?
19
+ ldap_person_user_wrapper(people_ou_entry)
20
+ else
21
+ nil
22
+ end
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module UcbRailsUser
2
+ module UserSessionManager
3
+
4
+ class InPeopleOuAddToUsersTable < ActiveInUserTable
5
+
6
+ def login(uid)
7
+ self.uid = uid
8
+
9
+ if people_ou_entry.present?
10
+ UcbRailsUser::UserLdapService.create_or_update_user_from_entry(people_ou_entry).tap do |user|
11
+ user.touch(:last_login_at)
12
+ end
13
+ else
14
+ nil
15
+ end
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,29 @@
1
+ module UcbRailsUser
2
+ module UserSessionManager
3
+
4
+ class LdapPersonUserWrapper
5
+ attr_accessor :ldap_person_entry
6
+
7
+ def initialize(ldap_person_entry)
8
+ self.ldap_person_entry = ldap_person_entry
9
+ end
10
+
11
+ def id
12
+ uid
13
+ end
14
+
15
+ def admin?
16
+ false
17
+ end
18
+
19
+ def method_missing(method, *args, &block)
20
+ if ldap_person_entry.respond_to?(method)
21
+ ldap_person_entry.send(method, *args, &block)
22
+ else
23
+ super
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ class User < ActiveRecord::Base
2
+ include UserConcerns
3
+
4
+ # Don't add anything more here - any logic for the User class should go into
5
+ # UserConcerns. This will make it much easier for host apps to customize
6
+ # behavior if they need to
7
+ # http://guides.rubyonrails.org/engines.html#implementing-decorator-pattern-using-activesupport-concern
8
+ end
@@ -0,0 +1,5 @@
1
+ %h1 Home
2
+
3
+ %p You are logged in as #{current_user.full_name}
4
+
5
+ %p= link_to "Logout", logout_path()
@@ -0,0 +1,5 @@
1
+ %h1 Home
2
+
3
+ %p You are not logged in.
4
+
5
+ %p= link_to "Login", login_path()
@@ -0,0 +1,19 @@
1
+ = form_tag('/ucb_rails/ldap_person_search', :class => "well form-inline", :id => 'lps-form', :method => :get, :remote => true) do
2
+
3
+ = hidden_field_tag('search-field-name')
4
+ = hidden_field_tag('result-link-http-method')
5
+ = hidden_field_tag('result-link-text')
6
+ = hidden_field_tag('result-link-class')
7
+ = hidden_field_tag('result-link-url')
8
+
9
+ .form-group
10
+ = label_tag 'first_name', 'First Name'
11
+ = text_field_tag("first_name", nil, class: "form-control")
12
+
13
+ .form-group
14
+ = label_tag 'last_name', 'Last Name'
15
+ = text_field_tag("last_name", nil, class: "form-control")
16
+
17
+ = submit_button_tag('Search', id: 'lps-search')
18
+ = button("Clear", url: "#", :id => 'lps-clear')
19
+
@@ -0,0 +1,9 @@
1
+ = modal(id: "lps-modal") do
2
+ = modal_header do
3
+ %h3.title Search For Person
4
+ = render partial: "ucb_rails_user/lps/form"
5
+ = modal_body do
6
+ #lps-results
7
+ = modal_footer do
8
+ #matches.pull-left
9
+ = modal_footer_close_button
@@ -0,0 +1,34 @@
1
+ = ucbr_table_tag(class: 'table-condensed') do
2
+ %thead
3
+ %tr
4
+ %th{style: 'width="1%"'}
5
+ %th First Name
6
+ %th Last Name
7
+ %th Email
8
+ %th Department
9
+ %tbody
10
+ - @lps_entries.each do |entry|
11
+ - entry_id = "ldap_uid_#{entry.uid}"
12
+ %tr{id: entry_id, class: ldap_entry_class(entry)}
13
+ %td= link_to_ldap_person_entry(entry)
14
+ %td= entry.first_name
15
+ %td= entry.last_name
16
+ %td= entry.email
17
+ %td= entry.departments
18
+
19
+ :javascript
20
+ $('#lps-modal table').dataTable({
21
+ "bPaginate": false,
22
+ "bFilter": false,
23
+ "bInfo": false,
24
+ "oLanguage": {
25
+ "sEmptyTable": "No matches for your search."
26
+ },
27
+ "aaSorting": [ [1, "asc"], [2, "asc"] ],
28
+ "aoColumnDefs": [
29
+ { "bSortable": false, "aTargets": [ 0 ] }, // selection link
30
+ { "aDataSort": [ 1, 2 ], "aTargets": [ 1 ] }, // first name
31
+ { "aDataSort": [ 2, 1 ], "aTargets": [ 2 ] }, // last name
32
+ { "aDataSort": [ 3, 1, 2 ], "aTargets": [ 3 ] }, // department
33
+ ]
34
+ });
@@ -0,0 +1,3 @@
1
+ - results = render :partial => 'ucb_rails_user/lps/results'
2
+ $('#lps-results').html('#{j results}');
3
+ $('#matches').html('#{lps_matches}');
@@ -0,0 +1,14 @@
1
+ = simple_form_for(@user, url: url, method: method, :html => {:class => 'form-horizontal'}, wrapper: :horizontal_form) do |f|
2
+ = f.input :ldap_uid, :input_html => {disabled: 'disabled'}
3
+ = f.input :first_name, :input_html => {disabled: 'disabled'}
4
+ = f.input :last_name, :input_html => {disabled: 'disabled'}
5
+ = f.input :email, :input_html => {disabled: 'disabled'}
6
+ = f.input :alternate_email
7
+ = f.input :last_login_at, as: :string, :input_html => {disabled: 'disabled', value: datetime_to_s(@user.last_login_at, :long)}
8
+ = f.input :superuser_flag, :as => :boolean, label: "Superuser?", wrapper: :horizontal_boolean
9
+ = f.input :inactive_flag, :as => :boolean, :label => "Inactive User?", wrapper: :horizontal_boolean
10
+
11
+ = form_actions do
12
+ = submit_button_tag("Update User")
13
+ = cancel_button_tag(url: admin_users_path)
14
+
@@ -0,0 +1,24 @@
1
+ %h3 Search Results
2
+ - if @results.empty?
3
+ %p No results matched your search
4
+ - else
5
+ %table.table.table-striped.add-user-search-results-table
6
+ %thead
7
+ %tr
8
+ %th UID
9
+ %th Employee ID
10
+ %th First Name
11
+ %th Last Name
12
+ %th
13
+ %tbody
14
+ - @results.each do |person|
15
+ %tr
16
+ %td= person.uid
17
+ %td= person.employee_id
18
+ %td= person.first_name
19
+ %td= person.last_name
20
+ %td
21
+ = form_tag admin_users_path(), method: :post do
22
+ = hidden_field_tag :ldap_uid, person.uid
23
+ = submit_tag "Add", class: "btn btn-primary"
24
+
@@ -0,0 +1,12 @@
1
+ %tr[user]
2
+ = td_bln(user.superuser?)
3
+ = td_bln(user.inactive?)
4
+ %td= user.first_name
5
+ %td= user.last_name
6
+ %td= mail_to(user.email)
7
+ %td.dt= datetime_to_s(user.last_login_at, :long)
8
+ %td.min= user.ldap_uid
9
+ %td.min= user.employee_id
10
+ %td= link_to('edit', edit_admin_user_path(user), :class => 'btn btn-default btn-xs', :id => dom_id(user, 'edit'))
11
+ %td= link_to('delete', admin_user_path(user), :data => {confirm: 'Are you sure?'}, :method => :delete, :class => 'btn btn-xs btn-danger')
12
+
@@ -0,0 +1,3 @@
1
+ %h1 Edit User
2
+
3
+ = render partial: 'form', locals: { url: admin_user_path(@user), method: :put }
@@ -0,0 +1,23 @@
1
+ .ucb-rails-users-table-header.clearfix
2
+ %h2.pull-left
3
+ Users
4
+ .pull-right
5
+ = link_to "Add new", new_admin_user_path(), class: "btn btn-primary"
6
+
7
+ %table.table.table-striped.table-bordered.table-hover.ucb-rails-users-table
8
+ %thead
9
+ %tr
10
+ %th.min Superuser?
11
+ %th.min Inactive?
12
+ %th First Name
13
+ %th Last Name
14
+ %th Email
15
+ %th.dt Last Login
16
+ %th UID
17
+ %th Employee ID
18
+ %th.min Edit
19
+ %th.min Delete
20
+ %tbody.highlight
21
+ = render partial: "ucb_rails_user/users/user", collection: @users
22
+
23
+
@@ -0,0 +1,16 @@
1
+ %h2 Add New User
2
+
3
+ = form_tag admin_user_search_path(), method: :get, remote: true, class: "form-inline user-search-form" do
4
+ = text_field_tag :first_name, "", placeholder: "First name", class: "form-control"
5
+ = text_field_tag :last_name, "", placeholder: "Last name", class: "form-control"
6
+ = text_field_tag :employee_id, "", placeholder: "Employee ID", class: "form-control"
7
+ = submit_tag "Search", class: "btn btn-primary"
8
+ = link_to "Cancel", admin_users_path(), class: "btn btn-default"
9
+
10
+ .ucb-rails-user-loader
11
+ Loading...
12
+
13
+ .search-results
14
+ - if @results
15
+ = render "results"
16
+
@@ -0,0 +1,4 @@
1
+ $('.ucb-rails-user-loader').hide()
2
+ $('.search-results').html("#{ j render(partial: "search_results") }")
3
+ addDatatablesToSearchResults()
4
+
@@ -0,0 +1,171 @@
1
+ require "simple_form"
2
+
3
+ # Use this setup block to configure all options available in SimpleForm.
4
+ SimpleForm.setup do |config|
5
+ # Wrappers are used by the form builder to generate a
6
+ # complete input. You can remove any component from the
7
+ # wrapper, change the order or even add your own to the
8
+ # stack. The options given below are used to wrap the
9
+ # whole input.
10
+ config.wrappers :default, class: :input,
11
+ hint_class: :field_with_hint, error_class: :field_with_errors do |b|
12
+ ## Extensions enabled by default
13
+ # Any of these extensions can be disabled for a
14
+ # given input by passing: `f.input EXTENSION_NAME => false`.
15
+ # You can make any of these extensions optional by
16
+ # renaming `b.use` to `b.optional`.
17
+
18
+ # Determines whether to use HTML5 (:email, :url, ...)
19
+ # and required attributes
20
+ b.use :html5
21
+
22
+ # Calculates placeholders automatically from I18n
23
+ # You can also pass a string as f.input placeholder: "Placeholder"
24
+ b.use :placeholder
25
+
26
+ ## Optional extensions
27
+ # They are disabled unless you pass `f.input EXTENSION_NAME => true`
28
+ # to the input. If so, they will retrieve the values from the model
29
+ # if any exists. If you want to enable any of those
30
+ # extensions by default, you can change `b.optional` to `b.use`.
31
+
32
+ # Calculates maxlength from length validations for string inputs
33
+ # and/or database column lengths
34
+ b.optional :maxlength
35
+
36
+ # Calculate minlength from length validations for string inputs
37
+ b.optional :minlength
38
+
39
+ # Calculates pattern from format validations for string inputs
40
+ b.optional :pattern
41
+
42
+ # Calculates min and max from length validations for numeric inputs
43
+ b.optional :min_max
44
+
45
+ # Calculates readonly automatically from readonly attributes
46
+ b.optional :readonly
47
+
48
+ ## Inputs
49
+ b.use :label_input
50
+ b.use :hint, wrap_with: { tag: :span, class: :hint }
51
+ b.use :error, wrap_with: { tag: :span, class: :error }
52
+
53
+ ## full_messages_for
54
+ # If you want to display the full error message for the attribute, you can
55
+ # use the component :full_error, like:
56
+ #
57
+ # b.use :full_error, wrap_with: { tag: :span, class: :error }
58
+ end
59
+
60
+ # The default wrapper to be used by the FormBuilder.
61
+ config.default_wrapper = :default
62
+
63
+ # Define the way to render check boxes / radio buttons with labels.
64
+ # Defaults to :nested for bootstrap config.
65
+ # inline: input + label
66
+ # nested: label > input
67
+ config.boolean_style = :nested
68
+
69
+ # Default class for buttons
70
+ config.button_class = 'btn'
71
+
72
+ # Method used to tidy up errors. Specify any Rails Array method.
73
+ # :first lists the first message for each field.
74
+ # Use :to_sentence to list all errors for each field.
75
+ # config.error_method = :first
76
+
77
+ # Default tag used for error notification helper.
78
+ config.error_notification_tag = :div
79
+
80
+ # CSS class to add for error notification helper.
81
+ config.error_notification_class = 'error_notification'
82
+
83
+ # ID to add for error notification helper.
84
+ # config.error_notification_id = nil
85
+
86
+ # Series of attempts to detect a default label method for collection.
87
+ # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
88
+
89
+ # Series of attempts to detect a default value method for collection.
90
+ # config.collection_value_methods = [ :id, :to_s ]
91
+
92
+ # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
93
+ # config.collection_wrapper_tag = nil
94
+
95
+ # You can define the class to use on all collection wrappers. Defaulting to none.
96
+ # config.collection_wrapper_class = nil
97
+
98
+ # You can wrap each item in a collection of radio/check boxes with a tag,
99
+ # defaulting to :span.
100
+ # config.item_wrapper_tag = :span
101
+
102
+ # You can define a class to use in all item wrappers. Defaulting to none.
103
+ # config.item_wrapper_class = nil
104
+
105
+ # How the label text should be generated altogether with the required text.
106
+ # config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
107
+
108
+ # You can define the class to use on all labels. Default is nil.
109
+ # config.label_class = nil
110
+
111
+ # You can define the default class to be used on forms. Can be overriden
112
+ # with `html: { :class }`. Defaulting to none.
113
+ # config.default_form_class = nil
114
+
115
+ # You can define which elements should obtain additional classes
116
+ # config.generate_additional_classes_for = [:wrapper, :label, :input]
117
+
118
+ # Whether attributes are required by default (or not). Default is true.
119
+ # config.required_by_default = true
120
+
121
+ # Tell browsers whether to use the native HTML5 validations (novalidate form option).
122
+ # These validations are enabled in SimpleForm's internal config but disabled by default
123
+ # in this configuration, which is recommended due to some quirks from different browsers.
124
+ # To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
125
+ # change this configuration to true.
126
+ config.browser_validations = false
127
+
128
+ # Collection of methods to detect if a file type was given.
129
+ # config.file_methods = [ :mounted_as, :file?, :public_filename ]
130
+
131
+ # Custom mappings for input types. This should be a hash containing a regexp
132
+ # to match as key, and the input type that will be used when the field name
133
+ # matches the regexp as value.
134
+ # config.input_mappings = { /count/ => :integer }
135
+
136
+ # Custom wrappers for input types. This should be a hash containing an input
137
+ # type as key and the wrapper that will be used for all inputs with specified type.
138
+ # config.wrapper_mappings = { string: :prepend }
139
+
140
+ # Namespaces where SimpleForm should look for custom input classes that
141
+ # override default inputs.
142
+ # config.custom_inputs_namespaces << "CustomInputs"
143
+
144
+ # Default priority for time_zone inputs.
145
+ # config.time_zone_priority = nil
146
+
147
+ # Default priority for country inputs.
148
+ # config.country_priority = nil
149
+
150
+ # When false, do not use translations for labels.
151
+ # config.translate_labels = true
152
+
153
+ # Automatically discover new inputs in Rails' autoload path.
154
+ # config.inputs_discovery = true
155
+
156
+ # Cache SimpleForm inputs discovery
157
+ # config.cache_discovery = !Rails.env.development?
158
+
159
+ # Default class for inputs
160
+ # config.input_class = nil
161
+
162
+ # Define the default class of the input wrapper of the boolean input.
163
+ config.boolean_label_class = 'checkbox'
164
+
165
+ # Defines if the default input wrapper class should be included in radio
166
+ # collection wrappers.
167
+ # config.include_default_input_wrapper_class = true
168
+
169
+ # Defines which i18n scope will be used in Simple Form.
170
+ # config.i18n_scope = 'simple_form'
171
+ end