diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 00000000..e69de29b
diff --git a/.gitignore b/.gitignore
index 1776a68d..8a796ddc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
.svn
-config.yml
+/config/config.yml
*.db
*.sqlite3
*.swp
@@ -7,6 +7,7 @@ config.yml
*.pidaproject
*.log
*.mo
+*.prod
pkg
ssl
custom/*
@@ -18,4 +19,4 @@ resources/dev
.bundle
.vagrant
Vagrantfile
-Gemfile.lock
+vendor/*
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 00000000..81aeb865
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,38 @@
+# We currently use Ruby 1.9.3 in prod, but bundle install is failing with that in dev.
+# We also can't use the newest ruby because it broke support for the syck gem in v2.2.
+FROM ruby:1.9
+
+#fix for jessie repo eol issues
+RUN echo "deb [check-valid-until=no] http://cdn-fastly.deb.debian.org/debian jessie main" > /etc/apt/sources.list.d/jessie.list
+RUN echo "deb [check-valid-until=no] http://archive.debian.org/debian jessie-backports main" > /etc/apt/sources.list.d/jessie-backports.list
+RUN sed -i '/deb http:\/\/deb.debian.org\/debian jessie-updates main/d' /etc/apt/sources.list
+
+# Note: gettext is installed to be able to use envsubst to inject config values
+RUN apt-get -o Acquire::Check-Valid-Until=false update -qq && apt-get install -y build-essential libpq-dev gettext vim
+
+# The rubycas Gemfile / gemspec doesn't specify a rails version since we use Apache Passenger modrails to run it in prod.
+# Need this installed in the container to run the dev version.
+#RUN gem install rails -v 3.2
+
+# This is the version staging and prod run. This base image uses 1.9.0.
+# Close enough that things should work, but update staging/prod if we have to get them in sync
+#RUN gem uninstall bundler
+#RUN gem install bundler -v 1.7.3
+
+RUN mkdir /app
+WORKDIR /app
+# Note: in .dockerignore we exclude Gemfile.lock b/c we want bundle install to regenerate it for
+# this version of Ruby. It doesn't get copied over to the container if you have one laying around from a non-Docker build.
+COPY Gemfile /app/Gemfile
+COPY Gemfile.lock /app/Gemfile.lock
+COPY rubycas-server.gemspec /app/rubycas-server.gemspec
+
+# See this article for why we copy to /tmp
+# We have a runtime script to check this so we can deal
+# with changes properly when gem versions get updated.
+# https://nickjanetakis.com/blog/dealing-with-lock-files-when-using-ruby-node-and-elixir-with-docker
+RUN bundle install && cp Gemfile.lock /tmp
+
+# Do this after bundle install b/c if we do it before then changing any files
+# causes bundle install to be invalidated and run again on the next build
+COPY . /app
diff --git a/Gemfile b/Gemfile
index 9f336726..a8b7a499 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,9 @@
source "http://rubygems.org"
gemspec
+#gem "mysql2"
+#gem "activerecord-mysql-adapter"
+gem "pg"
# Gems for authenticators
group :ldap do
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 00000000..edd558ff
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,127 @@
+PATH
+ remote: .
+ specs:
+ rubycas-server (1.1.3.pre)
+ activerecord (>= 2.3.12, < 4.0)
+ activesupport (>= 2.3.12, < 4.0)
+ crypt-isaac (~> 0.9.1)
+ pg (= 0.17.1)
+ sinatra (~> 1.0)
+ sinatra-r18n (~> 1.1.0)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activemodel (3.2.19)
+ activesupport (= 3.2.19)
+ builder (~> 3.0.0)
+ activerecord (3.2.19)
+ activemodel (= 3.2.19)
+ activesupport (= 3.2.19)
+ arel (~> 3.0.2)
+ tzinfo (~> 0.3.29)
+ activeresource (3.2.19)
+ activemodel (= 3.2.19)
+ activesupport (= 3.2.19)
+ activesupport (3.2.19)
+ i18n (~> 0.6, >= 0.6.4)
+ multi_json (~> 1.0)
+ addressable (2.3.6)
+ appraisal (0.4.1)
+ bundler
+ rake
+ arel (3.0.3)
+ builder (3.0.4)
+ capybara (1.1.2)
+ mime-types (>= 1.16)
+ nokogiri (>= 1.3.3)
+ rack (>= 1.0.0)
+ rack-test (>= 0.5.4)
+ selenium-webdriver (~> 2.0)
+ xpath (~> 0.1.4)
+ childprocess (0.5.3)
+ ffi (~> 1.0, >= 1.0.11)
+ crack (0.4.2)
+ safe_yaml (~> 1.0.0)
+ crypt-isaac (0.9.1)
+ diff-lcs (1.2.5)
+ ffi (1.9.3)
+ guard (1.4.0)
+ listen (>= 0.4.2)
+ thor (>= 0.14.6)
+ guard-rspec (2.0.0)
+ guard (>= 1.1)
+ rspec (~> 2.11)
+ i18n (0.6.11)
+ listen (0.7.3)
+ mime-types (2.3)
+ mini_portile (0.6.0)
+ multi_json (1.10.1)
+ net-ldap (0.1.1)
+ nokogiri (1.6.3.1)
+ mini_portile (= 0.6.0)
+ pg (0.17.1)
+ public_suffix (1.4.3)
+ r18n-core (1.1.11)
+ rack (1.6.11)
+ rack-protection (1.5.5)
+ rack
+ rack-test (0.6.3)
+ rack (>= 1.0)
+ rake (0.8.7)
+ rb-inotify (0.8.8)
+ ffi (>= 0.5.0)
+ rspec (2.99.0)
+ rspec-core (~> 2.99.0)
+ rspec-expectations (~> 2.99.0)
+ rspec-mocks (~> 2.99.0)
+ rspec-core (2.99.2)
+ rspec-expectations (2.99.2)
+ diff-lcs (>= 1.1.3, < 2.0)
+ rspec-mocks (2.99.2)
+ rubyzip (1.1.6)
+ safe_yaml (1.0.3)
+ selenium-webdriver (2.43.0)
+ childprocess (~> 0.5)
+ multi_json (~> 1.0)
+ rubyzip (~> 1.0)
+ websocket (~> 1.0)
+ sinatra (1.4.8)
+ rack (~> 1.5)
+ rack-protection (~> 1.4)
+ tilt (>= 1.3, < 3)
+ sinatra-r18n (1.1.11)
+ r18n-core (= 1.1.11)
+ sinatra (>= 1.3)
+ sqlite3 (1.3.9)
+ thor (0.19.1)
+ tilt (2.0.9)
+ tzinfo (0.3.55)
+ webmock (1.18.0)
+ addressable (>= 2.3.6)
+ crack (>= 0.3.2)
+ websocket (1.2.1)
+ xpath (0.1.4)
+ nokogiri (~> 1.3)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ activeresource (>= 2.3.12, < 4.0)
+ appraisal (~> 0.4.1)
+ capybara (= 1.1.2)
+ guard (~> 1.4.0)
+ guard-rspec (= 2.0.0)
+ net-ldap (~> 0.1.1)
+ nokogiri (= 1.6.3.1)
+ pg
+ public_suffix (= 1.4.3)
+ rack-test (= 0.6.3)
+ rake (= 0.8.7)
+ rb-inotify (~> 0.8.8)
+ rspec
+ rspec-core
+ rubycas-server!
+ sqlite3 (~> 1.3.1)
+ webmock (~> 1.8)
diff --git a/README.md b/README.md
index 1e355299..b34dddf3 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,23 @@
# RubyCAS-Server
+## Beyond Z Customizations
+
+The file lib/casserver/views/layout.erb has the login layout html. This is based on the main site, but it is modified, so must be maintained separately.
+
+The public/ folder has image and css assets brought off the main site. These are simply downloaded from the production site and renamed - to do this, load the join.bebraven.org site in your browser, view source and find the link rel=styleshet near the top. Download that file and save it in here as public/beyondz.css. They do NOT need to be maintained separately at this time. Currently required are the logo, favicon, and stylesheet.
+
+The file lib/beyondz.rb holds our authenticator. It uses a cooperative check_credentials http api on the public site to check against the main database. It is configured via config.yml for server (string), port (integer), ssl (boolean), and allow_self_signed (boolean) to know where to connect. The default ssl options is production-ready - it will verify certificates and use SSL. For development purposes, you may turn these options off with ssl: false.
+
+## End user flow
+
+The end user should always go to the service they want to use (portal.bebraven.org for example). The service then redirects them to the single sign on server, with a service parameter telling it to redirect them back once login is complete.
+
+user goes to canvas -> canvas sends them to sso -> sso sends back to canvas
+
+On the backend, the SSO server talks to the public site server and the service (canvas) server talks to the SSO server to validate login tickets. This should be SSL secured in production so the sso and canvas servers both need working client certificates, and the sso and public site servers need to be running https.
+
+The user master record is stored on the public site. User records also need to exist on the service - so a bz.org and canvas user need to exist with the same email address for the login to succeed end to end.
+
## Copyright
Portions contributed by Matt Zukowski are copyright (c) 2011 Urbacon Ltd.
@@ -36,3 +54,51 @@ If you have questions, try the [RubyCAS Google Group](https://groups.google.com/
RubyCAS-Server is licensed for use under the terms of the MIT License.
See the LICENSE file bundled with the official RubyCAS-Server distribution for details.
+
+## Running in a local development environment using Docker
+
+Edit `/etc/hosts` and add these values.
+```Shell
+127.0.0.1 joinweb
+127.0.0.1 ssoweb
+127.0.0.1 canvasweb
+```
+Bring up the Join server locally b/c this Docker container is configured
+to point at it for the user database / credentials. Do this by following
+the instructions [here](https://github.com/beyond-z/beyondz-platform#docker-setup)
+
+Then, from your application root just run:
+```Shell
+docker-compose up -d
+```
+When complete, the app will be available at: `http://ssoweb:3002`
+
+Note: the build will have a couple errors you can ignore. They don't
+seem to impact the functioning of the app. Just ignore:
+```Shell
+fatal: Not a git repository (or any of the parent directories): .git
+app/bin/rubycas-server maybe `gem pristine rubycas-server` will fix it?
+```
+
+Some things to keep in mind with Docker:
+* If there are build errors, run `docker-compose logs` to see what they
+ are.
+* The environment variables come from `docker-compose.yml` They are
+ injected into the container using `envsubst` in the
+`./docker-compose/scripts/docker_compose_run.sh` script.
+* If you change environment variables, rebuild to have them picked up by
+ running `./docker-compose/scripts/rebuild.sh
+* There are more scripts in `./docker-compose/scripts` to help you work
+ with the container(s).
+* If you change a file on the host (aka outside the container) it
+does not take effect inside the container. This application is rarely
+changed, so we don't mount a volume to allow files to be seamlessly
+changed inside and outside. To have a change take effect run
+`docker-compose/scripts/rebuild.sh`
+* Lastly, and this is IMPORTANT, the version of Ruby that we run on
+ production is 1.9.3. However, getting Docker building with that
+version has proven troublesome, so the Docker dev env runs Ruby 2.1. For
+that reason, DO NOT check-in the `Gemfile.lock` built on your local dev
+env or the update the `rubycas-server.gemspec`. If we have to rebuild
+gems on prod, we'll have to bite the bullet and upgrade the server (or
+consolidate the SSO server into the Join server
diff --git a/config/config.example.yml b/config/config.example.yml
index f49c5211..dd5842cd 100644
--- a/config/config.example.yml
+++ b/config/config.example.yml
@@ -18,6 +18,26 @@
# The following are example configurations for each of these three methods:
#
+# This is the domain of the main public website
+# It is used by the view to generate links back to the rest of the site on the login form.
+public_site_domain: join.bebraven.org
+
+# This is used to clear the login_message cookie after it is set by
+# a flash call on the Rails end. So it says like flash message saying
+# "your password is now reset". We read that cookie, display the message,
+# then clear the cookie so it isn't displayed twice. To clear the cookie,
+# we need to know the matching settings. cookie_domain is the configurable one.
+cookie_domain: .join.bebraven.org # this should be the *top* domain - so even for staging, it should be .join.bebraven.org still
+
+# If someone goes directly to the login url, this is where they end up
+# after a successful authentication. (They should never go directly there,
+# but copy/pasting the link could drop necessary form fields and get them
+# lost)
+default_service: http://yoursite.com/login/cas
+
+# This is the account number for google analytics. If this option is not
+# here, analytics script will not be added.
+google_analytics_account: UA-48011005-1
###
### WEBrick example
@@ -176,6 +196,21 @@ database:
# user_table: users
# username_column: username
# password_column: password
+#
+
+# Beyond Z Authenticator
+authenticator:
+ class: BeyondZ::CustomAuthenticator
+ source: beyondz.rb
+ server: join.bebraven.org
+ # We can override ssl, port, and self signed options for dev
+ # The default is to use SSL.
+ # ssl: false
+ # port: 80
+ # allow_self_signed: true
+
+
+
#
# When replying to a CAS client's validation request, the server will normally
# provide the client with the authenticated user's username. However it is
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 00000000..cb273a90
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,43 @@
+version: "3.5"
+services:
+
+ ssoweb:
+ build:
+ context: .
+ command: /app/docker-compose/scripts/docker_compose_run.sh
+ # Temporarily replace the command above with this if you want the container to just stay open
+ # so you can connect and troubleshoot b/c the container exits when you bring it up.
+ #tty: true
+ ports:
+ - "3002:3002"
+ # Make changes done outside the container reflect inside the container without needing a rebuild by mounting a volume.
+ volumes:
+ - .:/app
+ networks:
+ - bravendev
+ depends_on:
+ - ssodb
+ environment:
+ RACK_ENV: development
+ DATABASE_NAME: casserver
+ DATABASE_USER: postgres
+ DATABASE_PASSWORD:
+ DATABASE_HOST: ssodb
+
+ ssodb:
+ image: postgres:9.3
+ volumes:
+ - sso-db:/var/lib/postgresql/data
+ networks:
+ - bravendev
+ environment:
+ POSTGRES_DB: casserver
+
+# Note all Braven web app docker dev envs use this same network so they can talk to each other.
+# E.g. the hostname joinweb will resolve inside the ssoweb container if they are on the same docker network.
+networks:
+ bravendev:
+ name: braven_dev_network
+
+volumes:
+ sso-db:
diff --git a/docker-compose/config/config.yml b/docker-compose/config/config.yml
new file mode 100644
index 00000000..7f72ac3e
--- /dev/null
+++ b/docker-compose/config/config.yml
@@ -0,0 +1,587 @@
+# IMPORTANT NOTE ABOUT YAML CONFIGURATION FILES
+# ---> Be sure to use spaces instead of tabs for indentation. YAML is
+# white-space sensitive!
+
+##### SERVER SETUP ################################################################
+
+# There are several ways to run RubyCAS-Server:
+#
+# webrick -- stand-alone WEBrick server; should work out-of-the-box; this is
+# the default method, but probably not suited for high-traffic usage
+# mongrel -- stand-alone Mongrel server; fast, but you'll need to install
+# and compile Mongrel and run it behind an https reverse proxy like
+# Pound or Apache 2.2's mod_proxy (since Mongrel cannot serve out
+# over SSL on its own).
+# passenger -- served out by Apache via the mod_rails/mod_rack module
+# (see http://www.modrails.com/)
+#
+# The following are example configurations for each of these three methods:
+#
+
+# This is the domain of the main public website
+# It is used by the view to generate links back to the rest of the site on the login form.
+public_site_domain: joinweb
+
+# This is used to clear the login_message cookie after it is set by
+# a flash call on the Rails end. So it says like flash message saying
+# "your password is now reset". We read that cookie, display the message,
+# then clear the cookie so it isn't displayed twice. To clear the cookie,
+# we need to know the matching settings. cookie_domain is the configurable one.
+cookie_domain: .localhost # this should be the *top* domain - so even for staging, it should be .join.bebraven.org still
+
+# If someone goes directly to the login url, this is where they end up
+# after a successful authentication. (They should never go directly there,
+# but copy/pasting the link could drop necessary form fields and get them
+# lost)
+default_service: http://canvasweb/login/cas
+
+# This is the account number for google analytics. If this option is not
+# here, analytics script will not be added.
+#google_analytics_account: UA-48011005-1
+
+###
+### WEBrick example
+###
+# WEBrick is a simple, all-Ruby web server. This is the easiest method for running
+# RubyCAS-Server. All you need is an SSL certificate (enter its path under the
+# ssl_cert option). WEBrick is fine for sites with low to medium traffic, but for
+# high-performance scenarios you may want to look into deploying using Mongrel
+# or Passenger.
+
+server: webrick
+port: 3002
+#ssl_cert: /path/to/your/ssl.pem
+
+# If your private key is in a separate file from the cert
+
+#ssl_key: /path/to/your/private_key.pem
+
+# If you do not already have an SSL certificate and would like to automatically
+# generate one, run the "generate_ssl_certificate" rake task and use the following
+# settings:
+
+# ssl_cert: ssl/cert.pem
+# ssl_key: ssl/key.pem
+
+
+# By default the login page will be available at the root path
+# (e.g. https://login.example.net/). The uri_path option lets you serve it from a
+# different path (e.g. https://login.example.net/cas).
+
+#uri_path: /cas
+
+
+# This lets you bind the server to a specific address. Use 0.0.0.0 to listen on
+# all available interfaces (this is the default).
+
+#bind_address: 0.0.0.0
+
+
+###
+### Phusion Passenger (running under Apache configured for SSL)
+###
+
+# No additional configuration is requried to run RubyCAS-Server under
+# passsenger. Just follow the normal instructions for a Passenger app
+# (see http://www.modrails.com/).
+#
+# Here's an example Apache vhost config for RubyCAS-Server and Passenger:
+#
+# Listen 443
+#
+# ServerAdmin admin@example.net
+# ServerName login.example.net
+#
+# SSLEngine On
+# SSLCertificateFile /etc/apache2/ssl.crt/example.pem
+#
+# RailsAutoDetect off
+#
+# DocumentRoot /usr/lib/ruby/gems/1.8/gems/rubycas-server-0.8.0/public
+#
+#
+# AllowOverride all
+# Allow from all
+#
+#
+#
+
+
+##### DATABASE #################################################################
+
+# Set up the database connection. Make sure that this database is secure!
+#
+# By default, we use MySQL, since it is widely used and does not require any
+# additional ruby libraries besides ActiveRecord.
+#
+# With MySQL, your config would be something like the following:
+# (be sure to create the casserver database in MySQL beforehand,
+# i.e. `mysqladmin -u root create casserver`)
+
+database:
+ adapter: postgresql
+ database: ${DATABASE_NAME}
+ username: ${DATABASE_USER}
+ password: ${DATABASE_PASSWORD}
+ host: ${DATABASE_HOST}
+ reconnect: true
+
+#database:
+# adapter: mysql
+# database: casserver
+# username: root
+# password:
+# host: localhost
+# reconnect: true
+
+# IMPORTANT! By default, the server can handle up to ~5 concurrent requests
+# (without queuing). You can increase this by setting the database connection
+# pool size to a higher number. For example, to handle up to ~10 concurrent
+# requests:
+#
+#database:
+# pool: 10
+# adapter: mysql
+# database: casserver
+# username: root
+# password:
+# host: localhost
+
+#
+# Instead of MySQL you can use SQLite3, PostgreSQL, MSSQL, or anything else
+# supported by ActiveRecord.
+#
+# With SQLite3 (which does not require a separate database server), your
+# configuration would look something like the following (don't forget to install
+# the sqlite3-ruby gem beforehand!):
+
+#database:
+# adapter: sqlite3
+# database: /var/lib/casserver.db
+
+
+# By default RubyCAS-Server will run migrations at every startup to ensure
+# that its database schema is up-to-date. To disable this behaviour set
+# the following option to true:
+
+#disable_auto_migrations: true
+
+##### AUTHENTICATION ###########################################################
+
+# Configure how username/passwords are validated.
+#
+# !!! YOU MUST CONFIGURE AT LEAST ONE OF THESE AUTHENTICATION METHODS !!!
+#
+# There are several built-in methods for authentication:
+# SQL, ActiveDirectory, LDAP, and GoogleAccounts. If none of these work for you,
+# it is relatively easy to write your own custom Authenticator class (see below).
+#
+# === SQL Authentication =======================================================
+#
+# The simplest method is to validate against a SQL database. This assumes
+# that all of your users are stored in a table that has a 'username' column
+# and a 'password' column. When the user logs in, CAS connects to this database
+# and looks for a matching username/password in the users table. If a matching
+# username and password is found, authentication is successful.
+#
+# If you prefer to have your passwords stored in an encrypted form, have a
+# look at the SQLEncrypted authenticator:
+# http://code.google.com/p/rubycas-server/wiki/UsingTheSQLEncryptedAuthenticator
+#
+# If your users table stores passwords with MD5 hashing (for example as with
+# Drupal) try using the SQLMd5 version of the SQL authenticator.
+#
+# Example:
+#
+#authenticator:
+# class: CASServer::Authenticators::SQL
+# database:
+# adapter: mysql
+# database: some_database_with_users_table
+# username: root
+# password:
+# host: localhost
+# user_table: users
+# username_column: username
+# password_column: password
+#
+
+# Beyond Z Authenticator
+authenticator:
+ class: BeyondZ::CustomAuthenticator
+ source: beyondz.rb
+ # NOTE: in the local development environment, expose the Join server to this rubycas app by adding `127.0.0.1 joinweb` to your /etc/hosts file
+ server: joinweb
+ # We can override ssl, port, and self signed options for dev
+ # The default is to use SSL.
+ ssl: false
+ port: 3001
+ allow_self_signed: true
+
+
+
+#
+# When replying to a CAS client's validation request, the server will normally
+# provide the client with the authenticated user's username. However it is
+# possible for the server to provide the client with additional attributes.
+# You can configure the SQL authenticator to provide data from additional
+# columns in the users table by listing the names of the columns under the
+# 'extra_attributes' option. Note though that this functionality is experimental.
+# It should work with RubyCAS-Client, but may or may not work with other CAS
+# clients.
+#
+# For example, with this configuration, the 'full_name' and 'access_level'
+# columns will be provided to your CAS clients along with the username:
+#
+#authenticator:
+# class: CASServer::Authenticators::SQL
+# database:
+# adapter: mysql
+# database: some_database_with_users_table
+# user_table: users
+# username_column: username
+# password_column: password
+# extra_attributes: full_name, access_level
+#
+#
+#
+# === Google Authentication ====================================================
+#
+# The Google authenticator allows users to log in to your CAS server using
+# their Google account credentials (i.e. the same email and password they
+# would use to log in to Google services like Gmail). This authenticator
+# requires no special configuration -- just specify its class name:
+#
+#authenticator:
+# class: CASServer::Authenticators::Google
+#
+# If you are behind an http proxy, you can try specifying proxy settings as follows:
+#
+#authenticator:
+# class: CASServer::Authenticators::Google
+# proxy:
+# host: your-proxy-server
+# port: 8080
+# username: nil
+# password: nil
+#
+# If you want to restrict access to only those Google accounts in a
+# particular domain, as might be the case for an organization that
+# uses Google for its domain's e-mail, add the restricted_domain
+# option:
+#
+#authenticator:
+# class: CASServer::Authenticators::Google
+# restricted_domain: example.com
+#
+# Note that as with all authenticators, it is possible to use the Google
+# authenticator alongside other authenticators. For example, CAS can first
+# attempt to validate the account with Google, and if that fails, fall back
+# to some other local authentication mechanism.
+#
+# For example:
+#
+#authenticator:
+# - class: CASServer::Authenticators::Google
+# - class: CASServer::Authenticators::SQL
+# database:
+# adapter: mysql
+# database: some_database_with_users_table
+# username: root
+# password:
+# host: localhost
+# user_table: user
+# username_column: username
+# password_column: password
+#
+#
+# === ActiveDirectory Authentication ===========================================
+#
+# This method authenticates against Microsoft's Active Directory using LDAP.
+# You must configure the ActiveDirectory server, and base DN. The port number
+# and LDAP filter are optional. You must also enter a CN and password
+# for a special "authenticator" user. This account is used to log in to
+# the ActiveDirectory server and search LDAP. This does not have to be an
+# administrative account -- it only has to be able to search for other
+# users.
+#
+# Note that the auth_user parameter must be the user's CN (Common Name).
+# In Active Directory, the CN is genarally the user's full name, which is usually
+# NOT the same as their username (sAMAccountName).
+#
+# For example:
+#
+#authenticator:
+# class: CASServer::Authenticators::ActiveDirectoryLDAP
+# ldap:
+# host: ad.example.net
+# port: 389
+# base: dc=example,dc=net
+# filter: (objectClass=person)
+# auth_user: authenticator
+# auth_password: itsasecret
+#
+# A more complicated example, where the authenticator will use TLS encryption,
+# will ignore users with disabled accounts, and will pass on the 'cn' and 'mail'
+# attributes to CAS clients:
+#
+#authenticator:
+# class: CASServer::Authenticators::ActiveDirectoryLDAP
+# ldap:
+# host: ad.example.net
+# port: 636
+# base: dc=example,dc=net
+# filter: (objectClass=person) & !(msExchHideFromAddressLists=TRUE)
+# auth_user: authenticator
+# auth_password: itsasecret
+# encryption: simple_tls
+# extra_attributes: cn, mail
+#
+# It is possible to authenticate against Active Directory without the
+# authenticator user, but this requires that users type in their CN as
+# the username rather than typing in their sAMAccountName. In other words
+# users will likely have to authenticate by typing their full name,
+# rather than their username. If you prefer to do this, then just
+# omit the auth_user and auth_password values in the above example.
+#
+#
+# === LDAP Authentication ======================================================
+#
+# This is a more general version of the ActiveDirectory authenticator.
+# The configuration is similar, except you don't need an authenticator
+# username or password. The following example has been reported to work
+# for a basic OpenLDAP setup.
+#
+#authenticator:
+# class: CASServer::Authenticators::LDAP
+# ldap:
+# host: ldap.example.net
+# port: 389
+# base: dc=example,dc=net
+# username_attribute: uid
+# filter: (objectClass=person)
+#
+# If you need more secure connections via TSL, specify the 'encryption'
+# option and change the port. This example also forces the authenticator
+# to connect using a special "authenticator" user with the given
+# username and password (see the ActiveDirectoryLDAP authenticator
+# explanation above):
+#
+#authenticator:
+# class: CASServer::Authenticators::LDAP
+# ldap:
+# host: ldap.example.net
+# port: 636
+# base: dc=example,dc=net
+# filter: (objectClass=person)
+# encryption: simple_tls
+# auth_user: cn=admin,dc=example,dc=net
+# auth_password: secret
+#
+# If you need additional data about the user passed to the client (for example,
+# their 'cn' and 'mail' attributes, you can specify the list of attributes
+# under the extra_attributes config option:
+#
+#authenticator:
+# class: CASServer::Authenticators::LDAP
+# ldap:
+# host: ldap.example.net
+# port: 389
+# base: dc=example,dc=net
+# filter: (objectClass=person)
+# extra_attributes: cn, mail
+#
+# Note that the above functionality is somewhat limited by client compatibility.
+# See the SQL authenticator notes above for more info.
+#
+#
+# === Custom Authentication ====================================================
+#
+# It should be relatively easy to write your own Authenticator class. Have a look
+# at the built-in authenticators in the casserver/authenticators directory. Your
+# authenticator should extend the CASServer::Authenticators::Base class and must
+# implement a validate() method that takes a single hash argument. When the user
+# submits the login form, the username and password they entered is passed to
+# validate() as a hash under :username and :password keys. In the future, this
+# hash might also contain other data such as the domain that the user is logging
+# in to.
+#
+# To use your custom authenticator, specify it's class name and path to the
+# source file in the authenticator section of the config. Any other parameters
+# you specify in the authenticator configuration will be passed on to the
+# authenticator and made availabe in the validate() method as an @options hash.
+#
+# Example:
+#
+#authenticator:
+# class: FooModule::MyCustomAuthenticator
+# source: /path/to/source.rb
+# option_a: foo
+# another_option: yeeha
+#
+# === Multiple Authenticators ==================================================
+#
+# If you need to have more than one source for authentication, such as an LDAP
+# directory and a database, you can use multiple authenticators by making
+# :authenticator an array of authenticators.
+#
+#authenticator:
+# -
+# class: CASServer::Authenticators::ActiveDirectoryLDAP
+# ldap:
+# host: ad.example.net
+# port: 389
+# base: dc=example,dc=net
+# filter: (objectClass=person)
+# -
+# class: CASServer::Authenticators::SQL
+# database:
+# adapter: mysql
+# database: some_database_with_users_table
+# username: root
+# password:
+# host: localhost
+# user_table: user
+# username_column: username
+# password_column: password
+#
+# During authentication, the user credentials will be checked against the first
+# authenticator and on failure fall through to the second authenticator.
+#
+
+
+##### LOOK & FEEL ##############################################################
+
+# Set the path to the theme directory that determines how your CAS pages look.
+#
+# Custom themes are not well supported yet, but will be in the near future. In
+# the meantime, if you want to create a custom theme, you can create a
+# subdirectory under the CASServer's themes dir (for example,
+# '/usr/lib/ruby/1.8/gems/casserver-xxx/public/themes', if you installed CASServer
+# on Linux as a gem). A theme is basically just a theme.css file that overrides
+# the themes/cas.css styles along with a collection of image files
+# like logo.png and bg.png.
+#
+# By default, we use the 'simple' theme which you can find in themes/simple.
+theme: simple
+
+# The name of your company/organization. This will show up on the login page.
+organization: Braven Docker SSO
+
+# A short bit of text that shows up on the login page. You can make this blank
+# if you prefer to have no extra text shown at the bottom of the login box.
+infoline: Powered by RubyCAS-Server
+
+# Custom views directory. If set, this will be used instead of 'lib/casserver/views'.
+#custom_views: /path/to/custom/views
+
+# Custom public directory. If set, static content (css, etc.) will be served from here rather
+# than from rubycas-server's internal 'public' directory (but be mindful of any overriding
+# settings you may have in your web server's config).
+#public_dir: /path/to/custom/public
+
+##### LOCALIZATION (L10N) #######################################################
+# The server will attempt to detect the user's locale and show text in the
+# appropriate language based on:
+#
+# 1. The 'locale' URL parameter (if any)
+# 2. The 'locale' session (if any)
+# 3. The HTTP_ACCEPT_LANGUAGE header supplied by the user's browser.
+#
+# The format is the same as standard linux locales (langagecode_COUNTRYCODE):
+#
+# ru_RU - Russian, Russia
+# eo_AQ - Esperanto, Antarctica
+#
+# It will also work if you leave out the region (i.e. just "ru" for Russian,
+# "eo" for Esperanto).
+#
+# If you are interested in contributing new translations or have corrections
+# to the existing translations, see
+# https://github.com/rubycas/rubycas-server/wiki/Localization
+
+##### LOGGING ##################################################################
+
+# Configure general logging. This log is where you'll want to look in case of
+# problems.
+#
+# You may want to change the file to something like /var/log/casserver.log
+# Set the level to DEBUG if you want more detailed logging.
+
+log:
+ file: /var/log/casserver.log
+ level: DEBUG
+
+
+# If you want full database logging, uncomment this next section.
+# Every SQL query will be logged here. This is useful for debugging database
+# problems.
+
+#db_log:
+# file: /var/log/casserver_db.log
+
+
+# Setting the following option to true will disable CLI output to stdout.
+# i.e. this will get rid of messages like ">>> Redirecting RubyCAS-Server log..."
+# This is useful when, for example, you're running rspecs.
+
+#quiet: true
+
+
+##### SINGLE SIGN-OUT ##########################################################
+
+# When a user logs in to a CAS-enabled client application, that application
+# generally opens its own local user session. When the user then logs out
+# through the CAS server, each of the CAS-enabled client applications need
+# to be notified so that they can close their own local sessions for that user.
+#
+# Up until recently this was not possible within CAS. However, a method for
+# performing this notification was recently added to the protocol (in CAS 3.1).
+# This works exactly as described above -- when the user logs out, the CAS
+# server individually contacts each client service and notifies it of the
+# logout. Currently not all client applications support this, so this
+# behaviour is disabled by default. To enable it, uncomment the following
+# configuration line. Note that currently it is not possible to enable
+# or disable single-sign-out on a per-service basis, but this functionality
+# is planned for a future release.
+
+#enable_single_sign_out: true
+
+
+##### OTHER ####################################################################
+
+# You can set various ticket expiry times (specify the value in seconds).
+
+# Unused login and service tickets become unusable this many seconds after
+# they are created. (Defaults to 5 minutes)
+
+#maximum_unused_login_ticket_lifetime: 300
+#maximum_unused_service_ticket_lifetime: 300
+
+# The server must periodically delete old tickets (login tickets, service tickets
+# proxy-granting tickets, and ticket-granting tickets) to prevent buildup of
+# stale data. This effectively limits the maximum length of a CAS session to
+# the lifetime given here (in seconds). (Defaults to 48 hours)
+#
+# Note that this limit is not enforced on the client side; it refers only to the
+# the maximum lifetime of tickets on the CAS server.
+
+#maximum_session_lifetime: 172800
+
+
+# If you want the usernames entered on the login page to be automatically
+# downcased (converted to lowercase), enable the following option. When this
+# option is set to true, if the user enters "JSmith" as their username, the
+# system will automatically
+# convert this to "jsmith".
+
+#downcase_username: true
+
+# If you'd like to limit the service hosts that can use CAS for authentication,
+# add the individual IPs and IP ranges in CIDR notation below. Leaving this
+# setting blank will allow any server to authenticate users via the CAS server
+# and potentially harvest sensitive user information.
+
+#allowed_service_ips:
+# - 127.0.0.1
+# - 192.168.0.0/24
diff --git a/docker-compose/scripts/appconnect.sh b/docker-compose/scripts/appconnect.sh
new file mode 100755
index 00000000..a7db9414
--- /dev/null
+++ b/docker-compose/scripts/appconnect.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+docker-compose exec ssoweb /bin/bash
diff --git a/docker-compose/scripts/dbconnect.sh b/docker-compose/scripts/dbconnect.sh
new file mode 100755
index 00000000..fcc1756b
--- /dev/null
+++ b/docker-compose/scripts/dbconnect.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+# This connects to the development database. The user and database name are in docker-compose.yml
+docker-compose exec ssodb psql -U postgres casserver
diff --git a/docker-compose/scripts/docker_compose_run.sh b/docker-compose/scripts/docker_compose_run.sh
new file mode 100755
index 00000000..877e0f13
--- /dev/null
+++ b/docker-compose/scripts/docker_compose_run.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+# Take the environment variables setup in docker-compose.yml and inject their values
+# into the actual application config.yml file. Note: envsubst needs gettext to be installed.
+envsubst < /app/docker-compose/config/config.yml > /app/config/config.yml
+
+# Take from here: https://nickjanetakis.com/blog/dealing-with-lock-files-when-using-ruby-node-and-elixir-with-docker
+# To deal with problems when the Gemfile.lock changes in between runs of
+# bundle install
+set -e
+
+built_lock_file="/tmp/Gemfile.lock"
+current_lock_file="Gemfile.lock"
+
+function cp_built_lock_file() {
+ cp "${built_lock_file}" "${current_lock_file}"
+}
+
+if [ -f "${current_lock_file}" ]; then
+ diff="$(diff "${built_lock_file}" "${current_lock_file}")"
+ if [ "${diff}" != "" 2>/dev/null ]; then
+ cp_built_lock_file
+ fi
+else
+ cp_built_lock_file
+fi
+
+
+bundle exec rubycas-server -p 3002 -c /app/config/config.yml
diff --git a/docker-compose/scripts/rebuild.sh b/docker-compose/scripts/rebuild.sh
new file mode 100755
index 00000000..d0bf1d69
--- /dev/null
+++ b/docker-compose/scripts/rebuild.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+docker-compose down
+docker-compose up -d --force-recreate --build
diff --git a/docker-compose/scripts/restart.sh b/docker-compose/scripts/restart.sh
new file mode 100755
index 00000000..594a6154
--- /dev/null
+++ b/docker-compose/scripts/restart.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+docker-compose down
+docker-compose up -d
diff --git a/lib/beyondz.rb b/lib/beyondz.rb
new file mode 100644
index 00000000..e4057a23
--- /dev/null
+++ b/lib/beyondz.rb
@@ -0,0 +1,31 @@
+module BeyondZ
+ class CustomAuthenticator < CASServer::Authenticators::Base
+ def self.setup(options)
+ raise CASServer::AuthenticatorError, "Authenticator configuration needs server" unless options[:server]
+
+ @@server = options[:server]
+ @@ssl = (!options[:ssl].nil?) ? options[:ssl] : true
+ @@port = (!options[:port].nil?) ? options[:port] : (@@ssl ? 443 : 80)
+ @@allow_self_signed = (!options[:allow_self_signed].nil?) ? options[:allow_self_signed] : false
+ end
+
+ def validate(credentials)
+ http = Net::HTTP.new(@@server, @@port)
+ if @@ssl
+ http.use_ssl = true
+ if @@allow_self_signed
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE # self-signed cert would fail
+ end
+ end
+
+ request = Net::HTTP::Post.new('/users/check_credentials')
+ request.set_form_data(
+ 'username' => credentials[:username],
+ 'password' => credentials[:password]
+ )
+ response = http.request(request)
+
+ return (response.body == "true")
+ end
+ end
+end
diff --git a/lib/casserver/cas.rb b/lib/casserver/cas.rb
index d652d217..b308384d 100644
--- a/lib/casserver/cas.rb
+++ b/lib/casserver/cas.rb
@@ -304,7 +304,7 @@ def service_uri_with_ticket(service, st)
# removed, so that "http://google.com?ticket=12345&ticket=abcd" would be
# returned as "http://google.com?ticket=abcd".
def clean_service_url(dirty_service)
- return dirty_service if dirty_service.blank?
+ return settings.config[:default_service] if dirty_service.blank?
clean_service = dirty_service.dup
['service', 'ticket', 'gateway', 'renew'].each do |p|
clean_service.sub!(Regexp.new("&?#{p}=[^&]*"), '')
diff --git a/lib/casserver/server.rb b/lib/casserver/server.rb
index 77f7633a..567bb8d9 100644
--- a/lib/casserver/server.rb
+++ b/lib/casserver/server.rb
@@ -14,6 +14,18 @@ class Server < CASServer::Base
include CASServer::CAS # CAS protocol helpers
+ # We have to disable the iframe restriction to permit auto login
+ # in the Canvas resume module. The risk of this is minor because an
+ # iframe clickjacker wouldn't give the user a chance to enter their
+ # credentials anyway and even if they did, it is still sent to us!
+ #
+ # iframe restrictions are most meaningful on sites where a user is
+ # already logged in and one click can get them to do something nasty.
+ # That's just not the case on the sso server, with the exception of
+ # logout... but the worst that could do is annoy users, there's no
+ # risk of loss of information.
+ set :protection, :except => :frame_options
+
# Use :public_folder for Sinatra >= 1.3, and :public for older versions.
def self.use_public_folder?
Sinatra.const_defined?("VERSION") && Gem::Version.new(Sinatra::VERSION) >= Gem::Version.new("1.3.0")
@@ -278,6 +290,9 @@ def self.init_database!
content_type :html, 'charset' => 'utf-8'
@theme = settings.config[:theme]
@organization = settings.config[:organization]
+ @google_analytics_account = settings.config[:google_analytics_account]
+ @domain = settings.config[:public_site_domain]
+ @cookie_domain = settings.config[:cookie_domain]
@uri_path = settings.config[:uri_path]
@infoline = settings.config[:infoline]
@custom_views = settings.config[:custom_views]
@@ -398,7 +413,7 @@ def self.init_database!
@service = clean_service_url(params['service'])
# 2.2.2 (required)
- @username = params['username']
+ @username = params['username'].downcase # this ensures we always use lowercase for ease of case comparison throughout the system
@password = params['password']
@lt = params['lt']
@@ -513,7 +528,10 @@ def self.init_database!
# "logout" page, we take the user back to the login page with a "you have been logged out"
# message, allowing for an opportunity to immediately log back in. This makes it
# easier for the user to log out and log in as someone else.
- @service = clean_service_url(params['service'] || params['destination'])
+
+ # BZ modification: always use default service so logout/login goes back to our main
+ # site (which can redirect) regardless of where they came from
+ @service = clean_service_url(params['service'] || settings.config[:default_service]) # params['service'] || params['destination'])
@continue_url = params['url']
@gateway = params['gateway'] == 'true' || params['gateway'] == '1'
@@ -554,10 +572,21 @@ def self.init_database!
@message[:message] += t.notice.click_to_continue if @continue_url
+ @log_out_of_services = true
+
@lt = generate_login_ticket
if @gateway && @service
- redirect @service, 303
+ # I'm changing this to render the logout instead of redirecting
+ # because the redirection wouldn't give a chance for the logout
+ # iframe trick to log out of the main site. Since we also have
+ # a default service now in the configuration, it isn't as important
+ # for that to be preserved anyway - it will send them back to the
+ # default service which is good for us too as we can then handle
+ # the redirect based on the user account type automatically.
+
+ # redirect @service, 303
+ render @template_engine, :login
elsif @continue_url
render @template_engine, :logout
else
diff --git a/lib/casserver/views/_login_form.erb b/lib/casserver/views/_login_form.erb
index ab40351e..6eb32371 100644
--- a/lib/casserver/views/_login_form.erb
+++ b/lib/casserver/views/_login_form.erb
@@ -1,42 +1,32 @@
-<%# coding: UTF-8 -%>
+<% if @log_out_of_services %>
+
+
+<% end %>