UCB Rails User
A Rails engine that provides authentication and user management for UCB Rails apps. By adding this engine to your app, you get:
- a full User model which can be customized by host apps
- login and logout via CAS
- automatic creation of user records following CAS authentication
- controller filters that block access to resources unless user is logged in
- a default home page that reflects user's login status
- admin screens for updating and deleting user records
- ability for admins to impersonate other users
Conversion to UCPath
Version 2.0 and greater of this gem sets a user's
employee_id to the new UCPath employee id, rather than the legacy HCM employee id. If you need to use the older ID, use version 1.1.3 of this gem, or lower.
Upgrading to version 3.0+ from version 2.x
See this wiki page for details.
- Ruby >= 2.3
- Rails >= 5.1
Older versions may work as well, but they haven't been tested.
The easiest way to use this is to generate a new app with the ucb_rails command-line tool, which has this engine integrated as a gem and automates most of the steps below for you.
But if you need to add this to an already-existing app, do the following:
- Add the required gems to your Gemfile:
gem "ucb_rails_user", github: "ucb-ist-eas/ucb_rails_user" gem "ucb_ldap", github: "ucb-ist-eas/ucb-ldap"
bundler to install them:
RAILS_MASTER_KEYenvironment variable and setup an encrypted credentials file containing (at least) the credentials needed to connect to LDAP (ask another dev to help get this setup for you).
Add this line to your host app's
Copy this initializer file into your host app's
Setup a root path in
config/routes.rb, if you haven't already. You can use the default home page provided by this gem, if you like:
root to: UcbRailsUser::HomeController.action(:index)
- Copy the migrations for the
Impersonationmodels from the engine into your host app, and run them:
bin/rails railties:install:migrations bin/rake db:migrate
- Update your assets files
application.css add this line:
*= require ucb_rails_user/main
application.js add this line:
//= require ucb_rails_user/scripts
- Restart your host app as usual
You should be able to access the following routes:
/: if you're using
UcbRailsUser::HomeController#indexas your home page, you should see a simple screen showing your login status
/login: this should redirect you to the main CAS login page
/logout: this should log you out of CAS and redirect you back to the host app
/admin/users: this should display the user management screen, if your user account has the
superuserflag set; otherwise, you'll see a 401 page
/admin/impersonations: this is the page used to start impersonating another user (see below)
/admin/stop_impersonation: this stops an active impersonation
/admin/users/toggle_superuser: in dev mode, you can use this url to turn the superuser flag of your account on and off
The config/routes.rb file will show you the routes that this engine provides out of the box. But if necessary, you can override these routes in your host app.
For example, if the admin screens for your app are under the
/backend path rather than
/admin, you can rewrite the route in your host app like this:
resources :users, controller: "ucb_rails_user/users", path: "backend/users", as: :backend_users
Authentication during login is handled by UCB's central auth system and this gem uses the omniauth-cas to manage the handshake. Once that is completed, we're handed the LDAP uid of the authenticated user, and, by default, this gem will attempt to pull the user's employee data and create or update a
User record for them in the local database.
This behavior can be customized by writing your own user session manager. There are two steps to do this:
Create a subclass of
UcbRailsUser::UserSessionManager::Base. At a minimum you need to implement a
loginmethod that accepts the user's LDAP uid and returns a
Userinstance (or raises an error if the process fails), and a
current_usermethod that returns the
Userinstance for the currently-logged-in user. Several implementations are included so you can look to these to base yours off of.
config/initializers/ucb_rails_user.rbin your host app and change the
user_session_managerconfig setting to the class your custom implementation:
config.user_session_manager = "MyApp::MyCustomUserSessionManager"
The impersonation feature allows admins to be logged in as a different user in the system. This is useful when trying to diagnose data-specific problems, as the admin can see exactly what the user sees.
By default, this feature is only available to superusers, but you can change this by overriding the
User#can_impersonate? method and implementing any logic you prefer. See "Overriding Model And Controller Behavior" to see how to override methods in the
Determining Who The Real User Is
In the past, this gem provided a controller and helper method called
current_user which returned the
User record associated with the logged-in user. With the impersonation feature in place, this behavior changes slightly.
As of version 3.0,
current_user returns the currently logged-in user, unless that user is impersonating another user. In that case,
current_user will return the impersonated user (referred to as the impersonation "target" in the code). Most of our existing apps rely on
current_user to determine what should or should not be displayed, so the impersonation feature will work best for existing apps if
current_user returns the impersonated user.
Version 3.0 also adds a new method called
logged_in_user that always returns the actual logged-in user (whether or not that user is impersonating someone else).
Alice is an admin who has logged into the system:
Alice then starts impersonating Bob:
Alice stops impersonating Bob:
If you followed the setup instructions above, your
ApplicationController should be including
UcbRails::Concerns::ControllerMethods. This provides a number of utility methods you can use in your controllers:
current_user: returns the
Userinstance for the currently logged-in user, or
nilif user is not logged in. If the logged-in user is impersonating another user, this will return the impersonated user
logged_in_user: this returns the user who logged in with their Calnet credentials, even if that user is impersonating another user
current_ldap_person: returns the
UCB::LDAP::Personinstance for the currently logged-in user, or
nilif user is not logged in
logged_in?: returns true if the user is logged in
superuser?: returns true if the current user has the superuser flag set to true
ensure_authenticated_user: returns a 401 error if the user is not logged in
ensure_admin_user: returns a 401 error if the user is not a superuser
superuser? are all helper methods, so you can use them in views as well as controllers.
ensure_authenticated_user is set as a
before_filter so by default, all pages will require a login (except the
HomeController included in this gem).
To make a page available to a non-logged-in user, add this line to your controller:
ensure_admin_user as a
This engine comes with a few features to help with testing.
UcbRailsUser::UserSessionManager::TestSessionManager is a specialized session manager designed for test cases. It overrides the
login method to lookup the given
uid in the
users table of the database. As long as the user record already exists, the
login method will return successfully.
There is also a
UcbRailsUser::SpecHelpers module that provides some support methods. Specifically, the
login_user method can be used in request or integration specs to perform the behind-the-scenes work needed to login a given user. This method is implemented according to the omniauth gem documentation.
To add the testing support, add the following lines to your
# add this line require 'ucb_rails_user/spec_helpers' # then, somewhere in this block... RSpec.configure do |config| ... # ...add these lines config.include UcbRailsUser::SpecHelpers UcbRailsUser.config do |config| config.user_session_manager = "UcbRailsUser::UserSessionManager::TestSessionManager" end
Then, from within any request spec, you should be able to do this:
it "should do some neato feature" do user = create(:user) # assumes you've added FactoryBot or similiar login_user(user) end
and the user should now be logged in.
NOTE: For system specs, the logic is a little different - use
system_login_user rather than
login_user in these specs.
Overriding Model And Controller Behavior
The host app can add or override behavior of the
User model and
UserController as needed. We've followed the conventions suggested in the Rails guide to make this as easy as possible.
To add to the
User model, create a file named
app/models/user.rb in your host app, and add the following code:
class User < ApplicationRecord include UcbRailsUser::UserConcerns # add your code here end
Similarly, to override
app/controllers/ucb_rails/user_controller.rg and add the following:
class UcbRailsUser::UsersController < ApplicationController include UcbRailsUser::Concerns::UsersController # add your code here end
Overriding The Default Home Page
If you're using the
HomeController provided by this engine for your home page, you can change the views that are used for the logged in and not logged in states.
- create the directory
- inside that directory, create two files:
One of those two views will render when the user hits the home page, depending on whether or not they're currently logged in.