Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/saml_integration'
Browse files Browse the repository at this point in the history
  • Loading branch information
Siddarth R committed Aug 24, 2018
2 parents 1a0da5d + f60b287 commit 4b3b23c
Show file tree
Hide file tree
Showing 21 changed files with 387 additions and 42 deletions.
4 changes: 3 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ source 'https://rubygems.org'

gem 'bootstrap', '~> 4.0.0'
gem 'coffee-rails'
gem 'countries', require: 'countries/global'
gem 'devise'
gem 'figaro'
gem 'font-awesome-rails'
Expand All @@ -17,13 +18,14 @@ gem 'puma'
gem 'rails', '4.2.8'
gem 'redis'
gem 'rotp'
gem 'ruby-saml', '1.8.0'
gem 'saml_idp'
gem 'sass-rails'
gem 'sdoc', group: :doc
gem 'slim-rails'
gem 'turbolinks'
gem 'uglifier'
gem 'whenever', require: false

# platform :jruby do
# gem 'activerecord', '4.2.8'
# gem 'jdbc-mysql'
Expand Down
25 changes: 25 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ GEM
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.0.5)
countries (2.1.4)
i18n_data (~> 0.8.0)
money (~> 6.9)
sixarm_ruby_unaccent (~> 1.1)
unicode_utils (~> 1.4)
coveralls (0.7.2)
multi_json (~> 1.3)
rest-client (= 1.6.7)
Expand Down Expand Up @@ -107,6 +112,7 @@ GEM
hirb (0.7.3)
i18n (0.9.5)
concurrent-ruby (~> 1.0)
i18n_data (0.8.0)
jaro_winkler (1.5.1)
jbuilder (2.7.0)
activesupport (>= 4.2.0)
Expand All @@ -120,6 +126,8 @@ GEM
loofah (2.2.2)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
macaddr (1.7.1)
systemu (~> 2.6.2)
mail (2.7.0)
mini_mime (>= 0.1.1)
method_source (0.9.0)
Expand All @@ -130,6 +138,8 @@ GEM
mini_portile2 (2.3.0)
minitest (5.11.3)
mock_redis (0.18.0)
money (6.12.0)
i18n (>= 0.6.4, < 1.1)
multi_json (1.13.1)
multi_xml (0.6.0)
multipart-post (2.0.0)
Expand Down Expand Up @@ -241,7 +251,14 @@ GEM
rubocop-rspec (1.27.0)
rubocop (>= 0.56.0)
ruby-progressbar (1.9.0)
ruby-saml (1.8.0)
nokogiri (>= 1.5.10)
safe_yaml (1.0.4)
saml_idp (0.7.2)
activesupport (>= 3.2)
builder (~> 3.0)
nokogiri (>= 1.6.2)
uuid (~> 2.3)
sass (3.5.6)
sass-listen (~> 4.0.0)
sass-listen (4.0.0)
Expand All @@ -266,6 +283,7 @@ GEM
hirb
simplecov
simplecov-html (0.10.2)
sixarm_ruby_unaccent (1.2.0)
slim (3.0.9)
temple (>= 0.7.6, < 0.9)
tilt (>= 1.3.3, < 2.1)
Expand All @@ -280,6 +298,7 @@ GEM
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
systemu (2.6.5)
temple (0.8.0)
term-ansicolor (1.2.2)
tins (~> 0.8)
Expand All @@ -296,6 +315,9 @@ GEM
uglifier (4.1.11)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.4.0)
unicode_utils (1.4.0)
uuid (2.3.9)
macaddr (~> 1.0)
warden (1.2.7)
rack (>= 1.0)
web-console (3.3.0)
Expand All @@ -318,6 +340,7 @@ DEPENDENCIES
bootstrap (~> 4.0.0)
capybara
coffee-rails
countries
coveralls
database_cleaner
devise
Expand All @@ -342,6 +365,8 @@ DEPENDENCIES
rspec-rails
rubocop
rubocop-rspec
ruby-saml (= 1.8.0)
saml_idp
sass-rails
sdoc
shoulda-matchers
Expand Down
20 changes: 17 additions & 3 deletions app/controllers/organisations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def create
redirect_to organisations_path
else
flash[:errors] = org.errors.full_messages
redirect_to new_organisation_path
render :new, locals: { org: org }
end
end

Expand All @@ -26,14 +26,25 @@ def update
redirect_to organisations_path
else
flash[:errors] = org.errors.full_messages
redirect_to organisation_path(org)
render :show, locals: { org: org }
end
end

def show
render :show, locals: { org: load_org }
end

def setup_saml
org = load_org
if org.saml_setup?
flash[:errors] = 'SAML Certificates Already Setup'
else
load_org.setup_saml_certs
flash[:success] = 'Successfully setup SAML Certificates'
end
redirect_to organisations_path
end

private

def load_org
Expand All @@ -43,6 +54,9 @@ def load_org
end

def organisation_params
params.require(:organisation).permit(:name, :url, :email_domain)
params.require(:organisation).permit(
:name, :website, :domain, :country, :state, :address, :admin_email_address,
:slug, :unit_name
)
end
end
36 changes: 36 additions & 0 deletions app/controllers/saml_idp_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class SamlIdpController < SamlIdp::IdpController
layout false
before_action :setup_saml_configuration

private

def idp_authenticate(email, password)
User.find_and_check_user(email, password) ? User.find_active_user_by_email(email) : nil
end

def idp_make_saml_response(found_user)
encode_response found_user
end

def idp_logout
# user = User.by_email(saml_request.name_id)
# user.logout
end

def setup_saml_configuration
slug = params[:slug]
org = Organisation.find_by_slug(slug)
saml_url = "#{Figaro.env.gate_url}/#{slug}/saml"
SamlIdp.configure do |config|
config.x509_certificate = org.cert_key
config.secret_key = org.cert_private_key
config.organization_name = org.name
config.organization_url = org.website
config.base_saml_location = saml_url
config.attribute_service_location = "#{saml_url}/attributes"
config.single_service_post_location = "#{saml_url}/auth"
config.single_logout_service_post_location = "#{saml_url}/logout"
config.single_logout_service_redirect_location = "#{saml_url}/logout"
end
end
end
63 changes: 58 additions & 5 deletions app/models/organisation.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,71 @@
class Organisation < ActiveRecord::Base
validates :name, :url, :email_domain, presence: true
validates :name, :website, :domain, :country, :state, :address,
:admin_email_address, :slug, presence: true
validates :address, format: {
with: /\A[a-zA-Z0-9\s]+\z/,
message: 'Invalid - Only Alphabets, Space and Numbers Allowed',
}
validates :admin_email_address, email: true
validates :slug, uniqueness: true

attr_accessor :cert, :rsa_key

UPDATE_KEYS = %w(
name website domain country state address admin_email_address slug unit_name
).freeze

def self.find_by_slug(slug)
Organisation.where(slug: slug).first
end

def self.setup(attrs = {})
attrs.stringify_keys!
attrs = attrs.select { |k, _v| %w(name url email_domain).include?(k) }
attrs = attrs.stringify_keys
attrs = attrs.select { |k, _v| UPDATE_KEYS.include?(k) }
org = Organisation.new(attrs)
org.save if org.valid?
org
end

def update_profile(attrs = {})
attrs.stringify_keys!
attrs = attrs.select { |k, _v| %w(name url email_domain).include?(k) }
attrs = attrs.stringify_keys
attrs = attrs.select { |k, _v| UPDATE_KEYS.include?(k) }
assign_attributes(attrs)
save if valid?
end

def saml_setup?
cert_fingerprint.present? && cert_key.present? && cert_private_key.present?
end

def setup_saml_certs
return false unless persisted?
require 'openssl'
self.rsa_key = OpenSSL::PKey::RSA.new(2048)
private_key = rsa_key.to_pem
public_key = rsa_key.public_key
subject = "/C=#{country}/ST=#{state}/L=#{address}/O=#{name}/OU=#{unit_name}/CN=#{domain}"
self.cert = OpenSSL::X509::Certificate.new
cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
cert.not_before = Time.now
cert.not_after = Time.now + 365 * 24 * 60 * 60
cert.public_key = public_key
cert.serial = SecureRandom.random_number(10)
cert.version = 2
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = cert
cert.extensions = [
ef.create_extension('basicConstraints', 'CA:TRUE', true),
ef.create_extension('subjectKeyIdentifier', 'hash'),
]
cert.add_extension ef.create_extension(
'authorityKeyIdentifier', 'keyid:always,issuer:always'
)
cert.sign rsa_key, OpenSSL::Digest::SHA1.new
update_attributes(
cert_fingerprint: OpenSSL::Digest::SHA256.hexdigest(cert.to_der).scan(/../).join(':'),
cert_key: cert.to_pem,
cert_private_key: private_key
)
end
end
7 changes: 7 additions & 0 deletions app/validators/email_validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class EmailValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
unless value.to_s.match?(/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i)
record.errors[attribute] << (options[:message] || 'is not an email')
end
end
end
26 changes: 22 additions & 4 deletions app/views/organisations/_form.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,26 @@
= f.label :name
= f.text_field :name, class: 'form-control'
.form-group
= f.label :url
= f.text_field :url, class: 'form-control'
= f.label :website
= f.text_field :website, class: 'form-control'
.form-group
= f.label :email_domain
= f.text_field :email_domain, class: 'form-control'
= f.label :domain
= f.text_field :domain, class: 'form-control'
.form-group
= f.label :country
= f.collection_select :country, Country.all.sort_by(&:name), :gec, :name, {}, class: 'form-control'
.form-group
= f.label :state
= f.text_field :state, class: 'form-control'
.form-group
= f.label :address
= f.text_field :address, class: 'form-control'
.form-group
= f.label :admin_email_address
= f.text_field :admin_email_address, class: 'form-control'
.form-group
= f.label :slug
= f.text_field :slug, class: 'form-control'
.form-group
= f.label :unit_name
= f.text_field :unit_name, class: 'form-control'
11 changes: 9 additions & 2 deletions app/views/organisations/index.html.slim
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@
- if flash.key?(:success)
.alert.alert-success#organisation_form_success
= flash[:success]
- if flash.key?(:errors)
.alert.alert-danger#organisation_form_errors
= flash[:errors]
#organisation_list.table-responsive
table.table.table-striped
thead
tr
th Name
th URL
th Email Domain
th Actions
tbody
- if org_list.present?
- org_list.each do |org|
tr
td = link_to org.name, organisation_path(org)
td = link_to org.url, org.url
td = org.email_domain
td = link_to org.website, org.website
td = org.domain
td
- unless org.saml_setup?
= link_to "Setup SAML", organisation_setup_saml_path(org)
- else
td.text-center colspan='3'
p There are no organisations yet, why don't you create an organisation
Loading

0 comments on commit 4b3b23c

Please sign in to comment.