Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue 82]: Replace LDAP with SAML authenticatable #91

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ gem 'tzinfo-data'

gem 'pg'

gem 'devise_ldap_authenticatable'
gem 'devise_saml_authenticatable'

gem 'tinymce-rails'

Expand Down
2 changes: 1 addition & 1 deletion app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class User < ApplicationRecord

# Include default devise modules. Others available are:
# :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
devise :invitable, :ldap_authenticatable, :rememberable, :validatable
devise :invitable, :saml_authenticatable, :rememberable, :validatable

# Method added by Blacklight; Blacklight uses #to_s on your
# user class to get a user-displayable login/identifier for
Expand Down
3 changes: 3 additions & 0 deletions config/attribute-map.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# attribute-map.yml
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "user_name"
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "email"
101 changes: 95 additions & 6 deletions config/initializers/devise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@
# Many of these configuration options can be set straight in your model.
Devise.setup do |config|

config.ldap_logger = true ## Enable logging LDAP Query
config.ldap_create_user = true ## All valid AD users can log in and automatically create user records.
config.ldap_update_password = false ## Make sure that password change is not written back to AD.
config.ldap_use_admin_to_bind = true ## Use Administrator in bind at the time of LDAP authentication.
config.authentication_keys = [:email] ## Key used for authentication.
config.case_insensitive_keys = [:email] ## Does not distinguish case of key
config.strip_whitespace_keys = [:email] ## Delete the blanks contained in the key
Expand Down Expand Up @@ -236,7 +232,7 @@

# ==> Configuration for :validatable
# Range for password length.
config.password_length = 6..128
# config.password_length = 6..128

# Email regex used to validate email formats. It simply asserts that
# one (and only one) @ exists in the given string. This is mainly
Expand Down Expand Up @@ -282,7 +278,7 @@
# Time interval you can reset your password with a reset password key.
# Don't put a too small interval or your users won't have the time to
# change their passwords.
config.reset_password_within = 6.hours
# config.reset_password_within = 6.hours

# When set to false, does not sign a user in automatically after their password is
# reset. Defaults to true, so a user is signed in automatically after a reset.
Expand Down Expand Up @@ -366,4 +362,97 @@
# When set to false, does not sign a user in automatically after their password is
# changed. Defaults to true, so a user is signed in automatically after changing a password.
# config.sign_in_after_change_password = true

# ==> Configuration for :saml_authenticatable

# Create user if the user does not exist. (Default is false)
# Can also accept a proc, for ex:
# Devise.saml_create_user = Proc.new do |model_class, saml_response, auth_value|
# model_class == Admin
# end
config.saml_create_user = true

# Update the attributes of the user after a successful login. (Default is false)
# Can also accept a proc, for ex:
# Devise.saml_update_user = Proc.new do |model_class, saml_response, auth_value|
# model_class == Admin
# end
config.saml_update_user = true

# Lambda that is called if Devise.saml_update_user and/or Devise.saml_create_user are true.
# Receives the model object, saml_response and auth_value, and defines how the object's values are
# updated with regards to the SAML response.
# config.saml_update_resource_hook = -> (user, saml_response, auth_value) {
# saml_response.attributes.resource_keys.each do |key|
# user.send "#{key}=", saml_response.attribute_value_by_resource_key(key)
# end
#
# if (Devise.saml_use_subject)
# user.send "#{Devise.saml_default_user_key}=", auth_value
# end
#
# user.save!
# }

# Lambda that is called to resolve the saml_response and auth_value into the correct user object.
# Receives a copy of the ActiveRecord::Model, saml_response and auth_value. Is expected to return
# one instance of the provided model that is the matched account, or nil if none exists.
# config.saml_resource_locator = -> (model, saml_response, auth_value) {
# model.find_by(Devise.saml_default_user_key => auth_value)
# }

# Set the default user key. The user will be looked up by this key. Make
# sure that the Authentication Response includes the attribute.
config.saml_default_user_key = :email

# Optional. This stores the session index defined by the IDP during login. If provided it will be used as a salt
# for the user's session to facilitate an IDP initiated logout request.
config.saml_session_index_key = :session_index

# You can set this value to use Subject or SAML assertion as info to which email will be compared.
# If you don't set it then email will be extracted from SAML assertion attributes.
# config.saml_use_subject = true

# You can implement IdP settings with the options to support multiple IdPs and use the request object by setting this value to the name of a class that implements a ::settings method
# which takes an IdP entity id and a request object as arguments and returns a hash of idp settings for the corresponding IdP.
# config.idp_settings_adapter = "MyIdPSettingsAdapter"
config.idp_settings_adapter = nil

# You provide you own method to find the idp_entity_id in a SAML message in the case of multiple IdPs
# by setting this to the name of a custom reader class, or use the default.
# config.idp_entity_id_reader = "DeviseSamlAuthenticatable::DefaultIdpEntityIdReader"

# You can set the name of a class that takes the response for a failed SAML request and the strategy,
# and implements a #handle method. This method can then redirect the user, return error messages, etc.
# config.saml_failed_callback = "MySamlFailedCallbacksHandler"

# You can customize the named routes generated in case of named route collisions with
# other Devise modules or libraries. Set the saml_route_helper_prefix to a string that will
# be appended to the named route.
# If saml_route_helper_prefix = 'saml' then the new_user_session route becomes new_saml_user_session
# config.saml_route_helper_prefix = 'saml'

# You can add allowance for clock drift between the sp and idp.
# This is a time in seconds.
# config.allowed_clock_drift_in_seconds = 0

# In SAML responses, validate that the identity provider has included an InResponseTo
# header that matches the ID of the SAML request. (Default is false)
# config.saml_validate_in_response_to = false

config.saml_sign_out_success_url = ENV['SAML_SP_ENTITY_ID'] || "http://localhost:3000"

# Configure with your SAML settings (see ruby-saml's README for more information: https://github.com/onelogin/ruby-saml).
config.saml_configure do |settings|
settings.assertion_consumer_service_url = ENV['SAML_ASSERTION_CONSUMER_SERVICE_URL'] || "http://localhost:3000/users/saml/auth"
settings.assertion_consumer_service_binding = ENV['SAML_ASSERTION_CONSUMER_SERVICE_BINDING'] || "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
settings.issuer = ENV['SAML_ISSUER'] || "http://localhost:3000/users/saml/metadata"
settings.name_identifier_format = ENV['SAML_NAME_IDENTIFIER_FORMAT'] || "urn:oasis:names:tc:SAML:2.0:nameid-format:transient"
settings.sp_entity_id = ENV['SAML_SP_ENTITY_ID'] || "http://localhost:3000"
settings.authn_context = ENV['SAML_AUTHN_CONTEXT'] || "urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport"
settings.idp_slo_service_url = ENV['SAML_IDP_SLO_SERVICE_URL'] || "https://login.microsoftonline.com/68f381e3-46da-47b9-ba57-6f322b8f0da1/saml2"
settings.idp_sso_service_url = ENV['SAML_IDP_SSO_SERVICE_URL'] || "https://login.microsoftonline.com/68f381e3-46da-47b9-ba57-6f322b8f0da1/saml2"
settings.idp_cert_fingerprint = ENV['SAML_IDP_CERT_FINGERPRINT'] || "00:A1:2B:3C:44:55:6F:A7:88:CC:DD:EE:22:33:44:55:D6:77:8F:99"
settings.idp_cert_fingerprint_algorithm = ENV['SAML_IDP_CERT_FINGERPRINT_ALGORITHM'] || "http://www.w3.org/2000/09/xmldsig#sha1"
end
end
5 changes: 5 additions & 0 deletions db/migrate/20240625133800_add_session_index_to_users.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddSessionIndexToUsers < ActiveRecord::Migration[6.1]
def change
add_column :users, :session_index, :string
end
end
1 change: 1 addition & 0 deletions db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -372,6 +372,7 @@
t.integer "invited_by_id"
t.integer "invitations_count", default: 0
t.string "remember_token"
t.string "session_index"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["invitation_token"], name: "index_users_on_invitation_token", unique: true
t.index ["invitations_count"], name: "index_users_on_invitations_count"
Expand Down