nulogy_sso 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +90 -0
- data/Rakefile +22 -0
- data/app/assets/images/nulogy_sso/favicon.png +0 -0
- data/app/assets/stylesheets/nulogy_sso/sso_error.css +214 -0
- data/app/controllers/nulogy_sso/auth_controller.rb +100 -0
- data/app/services/nulogy_sso/authenticator.rb +61 -0
- data/app/views/sso_error.html.erb +45 -0
- data/config/initializers/inflections.rb +7 -0
- data/config/routes.rb +5 -0
- data/lib/nulogy_sso/controller_helper.rb +32 -0
- data/lib/nulogy_sso/engine.rb +29 -0
- data/lib/nulogy_sso/test_utilities/auth_mock.rb +94 -0
- data/lib/nulogy_sso/test_utilities/cert.der +0 -0
- data/lib/nulogy_sso/test_utilities/key.pem +52 -0
- data/lib/nulogy_sso/test_utilities/test_helper.rb +55 -0
- data/lib/nulogy_sso/version.rb +3 -0
- data/lib/nulogy_sso.rb +39 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +3 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
- data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +12 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/jobs/application_job.rb +2 -0
- data/spec/dummy/app/models/application_record.rb +3 -0
- data/spec/dummy/app/models/user.rb +5 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +36 -0
- data/spec/dummy/bin/update +31 -0
- data/spec/dummy/bin/yarn +11 -0
- data/spec/dummy/config/application.rb +41 -0
- data/spec/dummy/config/auth_sso.yml +20 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/cable.yml +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +49 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/content_security_policy.rb +25 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +33 -0
- data/spec/dummy/config/puma.rb +34 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/dummy/config/spring.rb +6 -0
- data/spec/dummy/config/storage.yml +34 -0
- data/spec/dummy/config.ru +5 -0
- data/spec/dummy/db/migrate/20190912211120_create_users.rb +12 -0
- data/spec/dummy/db/schema.rb +22 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/test.log +837 -0
- data/spec/dummy/package.json +5 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
- data/spec/dummy/public/apple-touch-icon.png +0 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/robots.txt +5 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-I/-IQzWZBVpyNJ9dwZO5AyzvfeT_L1FhLt_VKtEYstioM.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/-w/-wQxmZSFnAYuueO3wv-SMcI6GpkF0zb93Bk-K1nh6-I.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/03/03X-YlLKeQbKg9UgFMNeO-pNRjTrufgGcONruMJMhus.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/0v/0vQlHhWsvfiQYmfJVne6KDdCTrM9Ct1ZBUv1wI12fXc.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/1c/1cSa1S_Ik-wQeXi-Pb1sJX4_CC_Gu6CJ6THxM2ZxOTQ.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3A/3ACRNidm75IdG2lgWCRExd2yOtmLtKsdRrgsvML8gc4.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/3o/3ogX5PjkplRXrsO2QZTSkbohBKS3xuy3yHXRf30Vsho.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4J/4J2ZSEfqkzjpNHLlEPvd-5laPYz8eA0Tm6lt04YfVc4.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/4y/4ySQY2DeEDqx7mPwZF2m9dK9u5dtKOZj6PfLlWnRyEQ.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/5J/5JVr1JuiYXAwKzD64wxtFHroGbE9K2aMWb6f67tfVDQ.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/5u/5uuIL6SiWAHDNxQ63Bb-mEptuzUArLXyoTBDHJ5RTM4.cache +5 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/6b/6bIKFgIunI8QwnhOiCTyQOSHwPOCUNQbSzAVYhaqq8E.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8Q/8QavKcHzS_5D2q2DLfuAtFkJXKtNBAASd3oLAhmCNQs.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8a/8avIYxo88yDqslmUypali4ILLE-OVrryLchYsJuc9uE.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/8i/8izW0a4Zw0ZIpyJ2h1olnHKpWoSj6Yu39_6iziqxvzc.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/9J/9J6tFdjgXdjYE9Oc9ar9ti5spP_tdq-1_PiYhlD9t6I.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/9q/9qxBVp_Ackolv-Ir9TIGtC3Jg4cbqj6mxxLcoimAilw.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ac/AccyOixh8iQFR7XBSzGVVKalgdg64T1yeAp7NBwDtTg.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/An/An4y7wYtShMDQHwnRlEj4qcTQOlebrjVIrH-ZjRXlTw.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/D2/D2kPITeQ0WkknzjDQuPSbkXklgS6LtKcdtgyC3pu50k.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DO/DOjY5JRYb7u9e6jD6doIfYt5nrvUEjacjyhiTjXal2M.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/DU/DUMR4O60Nd02Z9uDvlfLbFUhubOiMbJnYrbJR_AKo4k.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Dt/Dt8q0fkQgUXw6I6nWfcPb4ZYGY-_UebKwhNObSrdkT8.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Ez/EzXtHEm-xBGVH72qLdvc7siqWebRocOJzp2iPwJm1TM.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/FP/FPL9y7t_40uxIV9w2IGRZJnEh_5N6NmXaOWLgvn2wmI.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/I-/I-aV6d9rIPlPNI2uOoWp7Il9fdzqpAmpJXcnd08TMxI.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/J0/J030bfDTH7OJpzAVdkIlvcx8n0gY-en4t63QpRh83Yo.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/KL/KLHTElxq3T60mK7oURgiZow2JiCXYvKCYEUdhhctZKM.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/L2/L2uA8R_LKv75JnGXHF4Z-0J1AGG5VObLLPSObFUwov0.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/M7/M7sD_tg1aMFHF-gHYiCM_ml6S44geHzvefKgdasAhz8.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/M7/m7xag7aON7xLeWIpCQBJ70HcdFqJ1ZUZUg46oU89Zfw.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/MA/MAsN3JftLnlWpSVqP84N8tvQ0c5DQ2AJBjL8nWVUINI.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Mo/Mo6cKTdohqOXUaEUYOXwkpUewtRun6oC_arZQltPGZg.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/OI/OIl8YRbsB6--l_CKIzlqpMNk31xMfPSe8IEGgNnNSQA.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/OJ/OJjACgGreSmoal4FQO9QFdjAyRFG8y8Q6urqK9jarR0.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/OS/OSjUgSyEOb3ZvdTPFeGkLQQzpdqEaIc_WDb9eOTANH8.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/PJ/PJg5JJZQpAq56ZK4RlLkLk3EO_C_PfhhcQcgX155Q3Y.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/QO/QO59HgQ2MIHck98-gaQqUM3ia4uzM138LlZsO5TsjLI.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Qb/QblQL05XuRs4iHdx96hOHN5mI_37pNeik6NWB99iuxY.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/R6/R678edDsoAWc6Bijbe0DN2gc--95iwiQo75-zILHS2Y.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Rv/RvFFKTRBnfZgEO-gWwKl_MUeogSPgvYvqivJkjtHF1c.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/St/StXiB3ZVbOvvg1eN7OMHH9g_uCljMP209iDbVkqVPaQ.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Uj/UjohEpOjNl1G6DoHx_DiqYWORyFy_SIY-Z_3iTgvaNY.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/W7/W7lea8dViN-zKiB0YDymngohwzY780_bafIrifdvdEU.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Wv/Wv5smlMR8HZlVpUVszeSJC2Z3y9tNVOCXkBcRXb3PK0.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/XZ/XZcPRDhkBL2jpJUwZciwFizBlxNz2CsMDBvM71F7Y6M.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Y4/Y4XV3TFQk_FtIsIDm735D9Gxqs2n-nVssI7ItEbHlT4.cache +4 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/YU/YUZTikjrtYByH5K9PQpw9o9incNNYKhsjekDQLhPniE.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Yp/YpMKz0G1Yf2acXo2K2vXpxKyND46cz69bF5q1zn5dWY.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Zk/Zk5OwH10pLNWcgDncIB2KDdKjiMNtG7tzFnAiL4tITw.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/Zs/ZsxGMLE5q7daf8xX9wxiISEfgaSSkWQG13xtjs5oGvU.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/_c/_c0OX2xSAUonHyl--ScIJdqu4dOIv5eWSiRCpsNIat4.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/_v/_vB_dHVUtju7m6FGk-MvQutohQFkt58fsmuf3Y1EFgU.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/aT/aTPoDhk5o9HqzSqn8VjurVSrWbYTbh3dAJmXhgLEZyc.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/aZ/aZgVZIZURvHb9-rOh2SivDHU-aBn-Jn1nKOPuI7lF0U.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bB/bBrouo-AbmaOmmI5w0EatyvJIj6Zel6BDOqpXUub-YM.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bC/bCGetG10_IPS-4Lea1FrjonfYd1QJf5j7J23dBSgxMI.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/bd/bde0sI4pua76cCQiJr5-4YPdoGOq7zUcE5JUFy10wEQ.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/c3/c3MkD92XKFFo7SIHfkh3Zf3b7TTV7PPhLy04Ak1iOuY.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/cI/cIXdHUyJ1umwlEmauLUx-whf_06eJvzlxEoPVPlG-4s.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ck/ckZOTlrjGXnk3bJ7dEgh6ld9VFFg1rV7MGXqEv1Fb9I.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/dC/dCxhpA2j6Pi1XDXa12EUmvdpLN_MJ3GPOZBqgg7GcVw.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ej/ejwFH71RbcAzfpFyRxO8czavHqoOs9SzC10c-PwS60Y.cache +4 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/fl/flex8byqqPRlAhCuyDZsaMg2gOeDj3NHAIzid-OS2Hg.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/gu/guEVVxdrH6uyyJ_m1VYsBVD566zTNeSBDgppqYGy-sk.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/hV/hVXlJWvvr6NsDbr1qABWR3CFv36YU7970jmxU_L2NuY.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ju/juWsFPPdDAbfJ4X3-dknffITj4HcW0Fu5U9UiaPn4b4.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/kG/kG0KlJGmHu0kwLwZDKon08APGF3Z3GpbRt6BuyKJUf4.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/kG/kGyYNYLbPlxoTk3K-8slRw-fuDznBFYnl-0y2jK6_cI.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/lS/lStvx9Beg2l4s8L-QdTSKuc5_qQPQAZPnIcRI7JkpPE.cache +4 -1
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/m9/m9S3GxTAovnGptmIbSxuSLVFWJCND5v6jetYN2IsHMQ.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/mJ/mJ-X19ke9W8_XFBJqqn9Hd4LZNsntZzZjcGZUyStW3s.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/n_/n_xYqQYhwEMQknb3jFQnjlxxBE9TzMNHCdJ-bEyZFIw.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/nx/nxTv3sKVUQZADJyM3dPaVmUA78MIsMLD_K279yN_GsI.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pM/pM_tvcMleZAXoskYgAVEJ8IPTDCzNUrpHakxVWTZPNQ.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/pt/ptleYqvprORReM2iFj-HW9O98Nhoy5qCF4iUWwtyve0.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/q6/q6VE6KTZtyGRe8Cuj9izRDMuieCcGKOKzBUH2niWYpc.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/q_/q_IgK4QolU1_5AK7pudGtHR2hiob-XkEZIMUgZY_iFM.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/qj/qjq5Ug3S2i5pydfGSIfUa7Y0s8s0FwqXzt0kkFijToI.cache +2 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/rd/rd8rR4dvQJUvFyyzVHdNbHilCTfHWlGn6kEU65JL2Fw.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/rx/rxqqFB2TXY1MQjq34u1hkkJgqenfyj2ZbYVtb2wEDvc.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sa/sab7V2jcUIn2nRakbXijIbvYmHyOOf6ShENXDRtYP1w.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/sr/sr3hnK_P9HaS68zSw0GLhpPzpgEs9shxEe6J7ktfbcw.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/u_/u_y6q_eS56FrB6fbIcFt49Tw9exxHbWGGb9puLTTcnk.cache +3 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ub/ubraBOWW9z9uvIXTsIrgJ3kzPboMmsZcyeNsv7sMCnU.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/wi/win_v0ZNu-l7AoRx8w3ls1xcy0mUExLBVMCSr5oaJFc.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/xH/xHr1kqSt7XnOPWH1LDZY1-0rIpwoT4OM4g68Ts3A9a4.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/yM/yMJd-lz8flnlwt-j5DOGjhykEGnynSCv4I2YKpPfr0Y.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/ys/ysn-zAA7CKGovpmCCejFwdSLV3RlH13Nc4cwac7BwL4.cache +0 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zT/zTQBjN2k1ltsiVOSJb4mqUhl4FvLQDM6cFRUtQLQ6Qw.cache +1 -0
- data/spec/dummy/tmp/cache/assets/sprockets/v3.0/zq/zqVwwFUvXqTxyX57DFJh5CY88HR0PEznhr-rLP6A5BQ.cache +2 -0
- data/spec/dummy/tmp/development_secret.txt +1 -0
- data/spec/examples.txt +19 -0
- data/spec/feature_spec_helper.rb +44 -0
- data/spec/features/nulogy_sso/sso_login_spec.rb +112 -0
- data/spec/integration/services/nulogy_sso/authenticator_spec.rb +95 -0
- data/spec/rails_helper.rb +28 -0
- data/spec/spec_helper.rb +43 -0
- data/spec/support/mock_auth0_verifier.rb +27 -0
- metadata +517 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4758a2972294221b9dc3fc1bcc3e6cc6bd6b08fb62649c42473947733eac8a26
|
4
|
+
data.tar.gz: 3c367990340d32e800658cb5893e50b239e72b7e647b5e14e7d48eddf58ed50c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d78af9ca9ae3db0346c647841a132fbcc49b07849934e84b205fa6742d1f7baffe8ae49c842715a9d327291577adcdd1eb3b8f835544798d27f685e6ce1f0fc0
|
7
|
+
data.tar.gz: '0927826539422b8428f4d5c2956b40dbc291d7e558dbe009579cb32468418bfaa24239228550c4d40aefd9b298edf5ac97a78c46d288d7d034849619e393bbe4'
|
data/README.md
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
# NulogySSO
|
2
|
+
|
3
|
+
**This repo is still under heavy initial development and is not ready to be used by any other product besides CPI. This status will be changed very shortly.**
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
This gem is a Rails Engine. It follows best practices [documented here](https://guides.rubyonrails.org/engines.html).
|
8
|
+
|
9
|
+
To begin with, add the gem to your Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem "nulogy_sso"
|
13
|
+
```
|
14
|
+
|
15
|
+
Install the gem:
|
16
|
+
|
17
|
+
```sh
|
18
|
+
bundle
|
19
|
+
```
|
20
|
+
|
21
|
+
Routes can now be mounted into your application in `routes.rb` and served up at a specific URI prefix:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
mount NulogySSO::Engine, at: "/sso"
|
25
|
+
|
26
|
+
# Optional redirects
|
27
|
+
get "login", to: redirect("sso/login")
|
28
|
+
get "logout", to: redirect("sso/logout")
|
29
|
+
```
|
30
|
+
|
31
|
+
The engine now needs to be configured. First create a YAML config file, perhaps named `config/auth_sso.yml`, to configure your app's Auth0 settings. This assumes that the necessary Auth0 applications have been created in the correct Auth0 tenants. The [CPI auth_sso.yml file](https://github.com/nulogy/Common-Platform-Interface/blob/master/config/auth_sso.yml) is a good starting place.
|
32
|
+
|
33
|
+
With that available, you can configure the engine with an initializer file. This is where _NulogySSO_ can be customized according to your application's needs. Put this code into `config/initializers/nulogy_sso.rb`, with the appropriate modifications implemented:
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
# Compiles config/auth_sso.yml into a Ruby object
|
37
|
+
NulogySSO.auth_config = Rails::Application.config_for(:auth_sso)
|
38
|
+
# Return the user matching the provided email
|
39
|
+
NulogySSO.find_user_by_email = ->(email) { nil }
|
40
|
+
# Return a boolean to indicate if the Auth0 user is valid for this app, or true if this step is not necessary
|
41
|
+
NulogySSO.validate_user = ->(user) { true }
|
42
|
+
```
|
43
|
+
|
44
|
+
The app is now ready to authenticate a user with Auth0! With NulogyAuth and Auth0, the user's identity is maintained across requests (and apps!) via a [JWT](https://auth0.com/docs/jwt) stored as a browser cookie. Add this code to the `ApplicationController`:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class ApplicationController < ActionController::Base
|
48
|
+
include NulogySSO::ControllerHelper
|
49
|
+
before_action :authenticate_sso_user
|
50
|
+
# ...
|
51
|
+
end
|
52
|
+
```
|
53
|
+
|
54
|
+
As an added bonus, NulogySSO also emulates the common Devise pattern of making the current User's user model object available via `current_user`. This is made available through inclusion of `ControllerHelper`.
|
55
|
+
|
56
|
+
## Development
|
57
|
+
|
58
|
+
### Setup
|
59
|
+
|
60
|
+
```sh
|
61
|
+
# Setup Ruby
|
62
|
+
rvm install $(cat .ruby-version)
|
63
|
+
bundle
|
64
|
+
|
65
|
+
# Setup project files
|
66
|
+
cp .env.sample .env
|
67
|
+
rake app:db:test:prepare
|
68
|
+
|
69
|
+
# Launch Docker containers, required for testing
|
70
|
+
docker-compose up -d
|
71
|
+
```
|
72
|
+
|
73
|
+
### Testing
|
74
|
+
|
75
|
+
There are multiple helpers made available via the `NulogyAuth::TestUtilities` module. These are helpful for doing things such as grabbing test JWT values and interacting with a [Mockserver](https://github.com/jamesdbloom/mockserver) mock of the Auth0 API.
|
76
|
+
|
77
|
+
It is a common use case for a Rails app to switch from Devise-powered authentication to Auth0. Here's a pattern that could be applied around a feature flag (e.g. environment variable) to switch between Devise user authentication test helpers and NulogyAuth test helpers: _(TODO: insert link to CPI `ControllerIntegrationSpecMacros`)_
|
78
|
+
|
79
|
+
### Contributing
|
80
|
+
|
81
|
+
Feel free to create a PR if you wish to add new functionality to this Engine or detect a bug. A developer on CN1 will review and merge.
|
82
|
+
|
83
|
+
This project follows [Semver Versioning](https://semver.org/). Please add an entry into [CHANGELOG.md](./CHANGELOG.md) in your PR. Deployments are done manually on an as-needed basis.
|
84
|
+
|
85
|
+
## Roadmap
|
86
|
+
|
87
|
+
* Parameterize app name for error page
|
88
|
+
* Buildkite pipeline
|
89
|
+
* Rubocop
|
90
|
+
* Rake install task (ie, generate required files automatically, instead of requiring a heavy amount of manual work to integrate nulogy_sso into a Rails app)
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require "bundler/setup"
|
3
|
+
rescue LoadError
|
4
|
+
puts "You must `gem install bundler` and `bundle install` to run rake tasks"
|
5
|
+
end
|
6
|
+
|
7
|
+
require "rdoc/task"
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = "rdoc"
|
11
|
+
rdoc.title = "NulogySSO"
|
12
|
+
rdoc.options << "--line-numbers"
|
13
|
+
rdoc.rdoc_files.include("README.md")
|
14
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
18
|
+
load "rails/tasks/engine.rake"
|
19
|
+
|
20
|
+
load "rails/tasks/statistics.rake"
|
21
|
+
|
22
|
+
require "bundler/gem_tasks"
|
Binary file
|
@@ -0,0 +1,214 @@
|
|
1
|
+
/**
|
2
|
+
* Do not edit directly
|
3
|
+
* Generated on Fri, 21 Jun 2019 19:45:11 GMT
|
4
|
+
*/
|
5
|
+
/* -----------------------------------------
|
6
|
+
## Default styles
|
7
|
+
----------------------------------------- */
|
8
|
+
html,
|
9
|
+
body {
|
10
|
+
margin: 0;
|
11
|
+
}
|
12
|
+
|
13
|
+
* {
|
14
|
+
box-sizing: border-box;
|
15
|
+
}
|
16
|
+
|
17
|
+
body {
|
18
|
+
color: #011e38;
|
19
|
+
font-family: "IBM Plex Sans", sans-serif;
|
20
|
+
line-height: 1.5;
|
21
|
+
}
|
22
|
+
|
23
|
+
code {
|
24
|
+
font-family: "IBM Plex Mono", monospace;
|
25
|
+
}
|
26
|
+
|
27
|
+
img {
|
28
|
+
max-width: 100%;
|
29
|
+
height: auto;
|
30
|
+
display: block;
|
31
|
+
}
|
32
|
+
|
33
|
+
/* -----------------------------------------
|
34
|
+
## Alerts
|
35
|
+
----------------------------------------- */
|
36
|
+
.Alert {
|
37
|
+
border-radius: 4px;
|
38
|
+
border-left: 4px solid #216beb;
|
39
|
+
padding: 16px;
|
40
|
+
background: #e1ebfa;
|
41
|
+
display: flex;
|
42
|
+
max-width: 432px;
|
43
|
+
}
|
44
|
+
.Alert__icon {
|
45
|
+
fill: #216beb;
|
46
|
+
}
|
47
|
+
.Alert a {
|
48
|
+
color: #011e38;
|
49
|
+
}
|
50
|
+
|
51
|
+
.Alert__title {
|
52
|
+
margin: 0;
|
53
|
+
font-weight: 600;
|
54
|
+
}
|
55
|
+
|
56
|
+
.Alert__message {
|
57
|
+
margin-top: 0;
|
58
|
+
}
|
59
|
+
.Alert__message:last-child {
|
60
|
+
margin-bottom: 0;
|
61
|
+
}
|
62
|
+
|
63
|
+
.Alert--danger__icon,
|
64
|
+
.Alert--success__icon {
|
65
|
+
margin-right: 8px;
|
66
|
+
}
|
67
|
+
|
68
|
+
.Alert--danger {
|
69
|
+
border-radius: 4px;
|
70
|
+
border-left: 4px solid #cc1439;
|
71
|
+
padding: 16px;
|
72
|
+
background: #fae6ea;
|
73
|
+
}
|
74
|
+
.Alert--danger__icon {
|
75
|
+
fill: #cc1439;
|
76
|
+
}
|
77
|
+
.Alert--danger a {
|
78
|
+
color: #011e38;
|
79
|
+
}
|
80
|
+
|
81
|
+
.Alert--success {
|
82
|
+
border-radius: 4px;
|
83
|
+
border-left: 4px solid #008059;
|
84
|
+
padding: 16px;
|
85
|
+
background: #e9f7f2;
|
86
|
+
}
|
87
|
+
.Alert--success__icon {
|
88
|
+
fill: #008059;
|
89
|
+
}
|
90
|
+
.Alert--success a {
|
91
|
+
color: #011e38;
|
92
|
+
}
|
93
|
+
|
94
|
+
.Alert--warning {
|
95
|
+
border-radius: 4px;
|
96
|
+
border-left: 4px solid #ffbb00;
|
97
|
+
padding: 16px;
|
98
|
+
background: #fcf5e3;
|
99
|
+
}
|
100
|
+
.Alert--warning__icon {
|
101
|
+
fill: #ffbb00;
|
102
|
+
}
|
103
|
+
.Alert--warning a {
|
104
|
+
color: #011e38;
|
105
|
+
}
|
106
|
+
|
107
|
+
/* -----------------------------------------
|
108
|
+
## Link
|
109
|
+
----------------------------------------- */
|
110
|
+
.Link {
|
111
|
+
color: #216beb;
|
112
|
+
}
|
113
|
+
.Link:hover {
|
114
|
+
cursor: pointer;
|
115
|
+
color: #1254c7;
|
116
|
+
}
|
117
|
+
.Link:focus {
|
118
|
+
outline: none;
|
119
|
+
box-shadow: 0px 0px 5px 0px rgba(33, 107, 235, 0.9);
|
120
|
+
}
|
121
|
+
|
122
|
+
/* -----------------------------------------
|
123
|
+
## Error page
|
124
|
+
----------------------------------------- */
|
125
|
+
.ErrorPage {
|
126
|
+
height: 100vh;
|
127
|
+
display: flex;
|
128
|
+
flex-direction: column;
|
129
|
+
padding: 16px;
|
130
|
+
padding-top: 24px;
|
131
|
+
}
|
132
|
+
|
133
|
+
.ErrorPage__body {
|
134
|
+
display: flex;
|
135
|
+
flex-direction: column;
|
136
|
+
flex-grow: 1;
|
137
|
+
}
|
138
|
+
|
139
|
+
.ErrorPage__container {
|
140
|
+
max-width: 432px;
|
141
|
+
margin: 0 auto;
|
142
|
+
}
|
143
|
+
|
144
|
+
.ErrorPage__header {
|
145
|
+
text-align: center;
|
146
|
+
margin-top: 16px;
|
147
|
+
margin-bottom: 40px;
|
148
|
+
}
|
149
|
+
|
150
|
+
.ErrorPage__logout {
|
151
|
+
margin-right: auto;
|
152
|
+
margin-left: auto;
|
153
|
+
margin-top: 30px;
|
154
|
+
}
|
155
|
+
@media screen and (min-width: 768px) {
|
156
|
+
.ErrorPage__header {
|
157
|
+
margin-top: 80px;
|
158
|
+
}
|
159
|
+
}
|
160
|
+
@media screen and (min-width: 1360px) {
|
161
|
+
.ErrorPage__header {
|
162
|
+
margin: 0;
|
163
|
+
}
|
164
|
+
}
|
165
|
+
|
166
|
+
.ErrorPage__logo {
|
167
|
+
display: block;
|
168
|
+
margin: 0 auto;
|
169
|
+
}
|
170
|
+
@media screen and (min-width: 1360px) {
|
171
|
+
.ErrorPage__logo--with-homepage-link {
|
172
|
+
margin-top: -32px;
|
173
|
+
}
|
174
|
+
}
|
175
|
+
|
176
|
+
.ErrorPage__main {
|
177
|
+
max-width: 432px;
|
178
|
+
margin: 0 auto;
|
179
|
+
text-align: center;
|
180
|
+
}
|
181
|
+
@media screen and (min-width: 1360px) {
|
182
|
+
.ErrorPage__main {
|
183
|
+
text-align: left;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
.ErrorPage__main .Alert {
|
187
|
+
text-align: left;
|
188
|
+
}
|
189
|
+
|
190
|
+
.ErrorPage__footer {
|
191
|
+
border-top: 1px solid #e4e7eb;
|
192
|
+
padding-top: 16px;
|
193
|
+
text-align: center;
|
194
|
+
font-size: 14px;
|
195
|
+
color: #434d59;
|
196
|
+
}
|
197
|
+
|
198
|
+
.Link--ErrorPage {
|
199
|
+
color: #00438f;
|
200
|
+
display: inline-block;
|
201
|
+
margin-top: 16px;
|
202
|
+
}
|
203
|
+
|
204
|
+
@media screen and (min-width: 1360px) {
|
205
|
+
.ErrorPage__body {
|
206
|
+
justify-content: center;
|
207
|
+
}
|
208
|
+
.ErrorPage__contents {
|
209
|
+
display: flex;
|
210
|
+
width: 672px;
|
211
|
+
margin: 0 auto;
|
212
|
+
align-items: center;
|
213
|
+
}
|
214
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "auth0"
|
4
|
+
|
5
|
+
module NulogySSO
|
6
|
+
class AuthController < ActionController::Base
|
7
|
+
include Auth0::Api::AuthenticationEndpoints
|
8
|
+
include Auth0::Mixins::HTTPProxy
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
# These instance variables have to be set in order for the HTTPProxy mixin to work.
|
12
|
+
@base_uri = auth_config.base_uri
|
13
|
+
@headers = { content_type: "application/json" }
|
14
|
+
end
|
15
|
+
|
16
|
+
def login
|
17
|
+
raw_access_token = cookies[NulogySSO.auth_cookie_key]
|
18
|
+
|
19
|
+
authenticator.validate_token(
|
20
|
+
raw_access_token,
|
21
|
+
on_success: method(:on_authentication_success),
|
22
|
+
on_invalid_token: -> { redirect_to auth_path }
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def code
|
27
|
+
code = params.require(:code)
|
28
|
+
begin
|
29
|
+
raw_access_token = token_response(code)["access_token"]
|
30
|
+
rescue Auth0::Exception => e
|
31
|
+
return sso_error
|
32
|
+
end
|
33
|
+
|
34
|
+
authenticator.validate_token(
|
35
|
+
raw_access_token,
|
36
|
+
on_success: method(:on_authentication_success),
|
37
|
+
on_invalid_token: -> { sso_error }
|
38
|
+
)
|
39
|
+
end
|
40
|
+
|
41
|
+
def logout
|
42
|
+
cookies.delete(NulogySSO.auth_cookie_key, domain: :all)
|
43
|
+
|
44
|
+
query_params = {
|
45
|
+
returnTo: auth_config.redirect_uri, # Yes, this must be camelCased
|
46
|
+
client_id: auth_config.client_id
|
47
|
+
}
|
48
|
+
redirect_to "#{auth_config.base_uri}/v2/logout?#{query_params.to_query}"
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
delegate :auth_config, to: :NulogySSO
|
54
|
+
|
55
|
+
def sso_error
|
56
|
+
render status: :forbidden, template: "sso_error"
|
57
|
+
end
|
58
|
+
|
59
|
+
def authenticator
|
60
|
+
@authenticator ||= Authenticator.new
|
61
|
+
end
|
62
|
+
|
63
|
+
def on_authentication_success(access_token)
|
64
|
+
respond_with_cookies(access_token)
|
65
|
+
|
66
|
+
redirect_to params["origin"].presence || auth_config.redirect_uri
|
67
|
+
end
|
68
|
+
|
69
|
+
def token_response(code)
|
70
|
+
exchange_auth_code_for_tokens(
|
71
|
+
code,
|
72
|
+
redirect_uri: auth_config.login_uri,
|
73
|
+
client_id: auth_config.client_id,
|
74
|
+
client_secret: auth_config.client_secret
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def respond_with_cookies(access_token_value)
|
79
|
+
cookies[NulogySSO.auth_cookie_key] = {
|
80
|
+
value: access_token_value,
|
81
|
+
domain: :all,
|
82
|
+
expires: 36_000.seconds, # TODO: Fetch this value from the JWT
|
83
|
+
httponly: true,
|
84
|
+
secure: request.ssl?
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
def auth_path
|
89
|
+
query_params = {
|
90
|
+
audience: auth_config.audience,
|
91
|
+
client_id: auth_config.client_id,
|
92
|
+
response_type: "code",
|
93
|
+
scope: "openid email",
|
94
|
+
redirect_uri: "#{auth_config.login_uri}?origin=#{session[:previous_request_url]}"
|
95
|
+
}
|
96
|
+
|
97
|
+
"#{auth_config.base_uri}/authorize?#{query_params.to_query}"
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "auth0_rs256_jwt_verifier"
|
4
|
+
|
5
|
+
module NulogySSO
|
6
|
+
class Authenticator
|
7
|
+
ACCESS_TOKEN_VERIFIER = Auth0RS256JWTVerifier.new(
|
8
|
+
issuer: "#{NulogySSO.auth_config.base_uri}/", # Auth0 requires a backslash on the Issuer
|
9
|
+
audience: NulogySSO.auth_config.audience,
|
10
|
+
jwks_url: "#{NulogySSO.auth_config.base_uri}/.well-known/jwks.json"
|
11
|
+
)
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
verifier: ACCESS_TOKEN_VERIFIER,
|
15
|
+
find_user_by_email: NulogySSO.find_user_by_email,
|
16
|
+
validate_user: NulogySSO.validate_user
|
17
|
+
)
|
18
|
+
@verifier = verifier
|
19
|
+
@find_user_by_email = find_user_by_email
|
20
|
+
@validate_user = validate_user
|
21
|
+
end
|
22
|
+
|
23
|
+
# Authorizes the provided JWT, ensuring that a valid user can be associated to the token
|
24
|
+
def validate_token(raw_access_token, on_success:, on_invalid_token:)
|
25
|
+
access_token = decoded_validated_access_token(raw_access_token)
|
26
|
+
|
27
|
+
return on_invalid_token.call if access_token.nil?
|
28
|
+
|
29
|
+
user = fetch_user(access_token)
|
30
|
+
return on_invalid_token.call if user.blank? || !validate_user.call(user)
|
31
|
+
|
32
|
+
on_success.call(access_token)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Returns the authenticated user that matches the provided JWT, or nil if the user cannot be authenticated
|
36
|
+
def authenticated_user(raw_access_token)
|
37
|
+
access_token = decoded_validated_access_token(raw_access_token)
|
38
|
+
|
39
|
+
return nil if access_token.nil?
|
40
|
+
|
41
|
+
fetch_user(access_token)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
attr_reader :verifier, :find_user_by_email, :validate_user
|
47
|
+
|
48
|
+
def decoded_validated_access_token(raw_access_token)
|
49
|
+
if raw_access_token.present? && verifier.verify(raw_access_token).valid?
|
50
|
+
return JSON::JWT.decode(raw_access_token, :skip_verification)
|
51
|
+
end
|
52
|
+
|
53
|
+
nil
|
54
|
+
end
|
55
|
+
|
56
|
+
def fetch_user(access_token)
|
57
|
+
email = access_token.fetch(NulogySSO::JWT_EMAIL_KEY)
|
58
|
+
find_user_by_email.call(email)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
7
|
+
<title>Error</title>
|
8
|
+
<%= favicon_link_tag "nulogy_sso/favicon.png" %>
|
9
|
+
<%= stylesheet_link_tag "nulogy_sso/sso_error" %>
|
10
|
+
<link href="https://fonts.googleapis.com/css?family=IBM+Plex+Sans:300,400,500,600&display=swap" rel="stylesheet">
|
11
|
+
</head>
|
12
|
+
|
13
|
+
<body>
|
14
|
+
<div class="ErrorPage">
|
15
|
+
<div class="ErrorPage__body">
|
16
|
+
<div class="ErrorPage__contents">
|
17
|
+
<header class="ErrorPage__header">
|
18
|
+
<svg class="ErrorPage__logo" height="48px" width="200px" viewBox="0 0 133 32"><path fill="#F0B41C" d="M30.6967273,1.13648485 L36.3810909,3.40945455 L36.3810909,23.8758788 C36.3810909,28.2705455 30.9507879,29.0424242 27.2853333,29.5602424 C29.3818182,29.0424242 30.7083636,28.4606061 30.6967273,23.8758788 L30.6967273,5.68436364 L25.0123636,3.40945455 L30.6967273,1.13648485 Z M6.82084848,28.4237576 L6.82084848,15.9204848 C6.82084848,14.6618182 7.76533333,13.238303 8.91151515,12.7476364 L14.7801212,10.2264242 L14.7801212,18.1779394 L20.4644848,21.6048485 C21.6106667,22.1866667 23.8758788,22.2002424 23.8758788,20.4664242 L23.8758788,17.0550303 L21.5990303,15.9166061 L21.5990303,1.56319402e-13 L4.26666667,6.38642424 C1.91030303,7.25333333 3.55271368e-15,9.98593939 3.55271368e-15,12.5071515 L3.55271368e-15,31.2669091 L6.82084848,28.4237576 Z"></path><g transform="translate(43.000000, 0.000000)" fill="#1C68A5"><path d="M5.14521212,24.2224981 L1.33226763e-14,24.2224981 L1.33226763e-14,9.41134654 L4.75151515,9.41134654 L4.75151515,11.8937708 C5.43733518,11.0060378 6.29475204,10.2653615 7.27272727,9.71583139 C8.16188898,9.23817548 9.15699751,8.9920662 10.166303,9.00019502 C11.7333333,9.00019502 12.9221818,9.44948795 13.7328485,10.3480738 C14.5435152,11.2466597 14.9488485,12.5615688 14.9488485,14.2928011 L14.9488485,24.2224981 L9.83078788,24.2224981 L9.83078788,15.7376496 C9.83078788,14.961892 9.67175758,14.3755486 9.35369697,13.9786193 C9.03563636,13.58169 8.56436364,13.3877506 7.93987879,13.3968011 C7.04258586,13.3968011 6.35216162,13.6980536 5.86860606,14.3005587 C5.38505051,14.9030637 5.14391919,15.7803162 5.14521212,16.9323162 L5.14521212,24.2224981 Z"></path><path d="M27.8366061,9 L32.9507879,9 L32.9507879,22.6203636 L28.1992727,24.5190303 L28.1992727,21.6254545 C27.5124636,22.5143219 26.6553593,23.2573653 25.6780606,23.8111515 C24.7938668,24.2845189 23.8048332,24.5279425 22.8019394,24.5190303 C21.2323232,24.5190303 20.0402424,24.0665051 19.225697,23.1614545 C18.4111515,22.256404 18.0025859,20.9408485 18,19.2147879 L18,9 L23.1452121,9 L23.1452121,17.7796364 C23.1452121,18.5553939 23.3042424,19.1443232 23.622303,19.5464242 C23.9403636,19.9485253 24.4116364,20.1482828 25.0361212,20.145697 C25.9411717,20.145697 26.631596,19.8418586 27.1073939,19.2341818 C27.5831919,18.6265051 27.8243232,17.7524848 27.8307879,16.6121212 L27.8366061,9 Z"></path><polygon id="Path" points="38.7151515 24.5546667 36 24.5546667 36 1.088 38.7151515 1.15463195e-13"></polygon><path d="M41,17.0717576 C41,14.7263838 41.7453737,12.7947475 43.2361212,11.2768485 C44.7268687,9.75894949 46.6171313,9 48.9069091,9 C51.1966869,9 53.0869495,9.75894949 54.577697,11.2768485 C56.0684444,12.7947475 56.8138182,14.7263838 56.8138182,17.0717576 C56.8138182,19.4274747 56.0684444,21.3668687 54.577697,22.8899394 C53.0869495,24.4130101 51.1966869,25.1719596 48.9069091,25.1667879 C46.6261818,25.1667879 44.7385051,24.4078384 43.2438788,22.8899394 C41.7492525,21.3720404 41.0012929,19.4326465 41,17.0717576 Z M43.6957576,17.0717576 C43.6957576,18.9141818 44.172202,20.3965253 45.1250909,21.5187879 C46.0779798,22.6410505 47.327596,23.2028283 48.8739394,23.2041212 C50.4254545,23.2041212 51.6802424,22.6423434 52.638303,21.5187879 C53.5963636,20.3952323 54.0786263,18.9128889 54.0850909,17.0717576 C54.0850909,15.2422626 53.6028283,13.7657374 52.638303,12.6421818 C51.6737778,11.5186263 50.4189899,10.9574949 48.8739394,10.9587879 C47.3224242,10.9587879 46.0728081,11.5205657 45.1250909,12.6441212 C44.1773737,13.7676768 43.7009293,15.2435556 43.6957576,17.0717576 Z"></path><path d="M70.0824261,23.582303 C69.3285884,24.0285568 68.5220417,24.3790594 67.6814564,24.625697 C66.9065592,24.8487063 66.1042899,24.9622904 65.2979413,24.9631515 C63.1180625,24.9631515 61.3564463,24.2397576 60.0130928,22.7929697 C58.6697392,21.3461818 57.9987089,19.4449293 58.0000019,17.0892121 C58.0000019,14.6882424 58.7175776,12.739798 60.1527291,11.2438788 C61.5878807,9.7479596 63.4626281,9 65.7769716,9 C66.5067857,9.0106757 67.2345208,9.08072506 67.9529716,9.20945455 C68.7804463,9.34650505 69.737214,9.55725253 70.8232746,9.84169697 L72.745214,9.06593939 L72.745214,22.4283636 C72.745214,24.213899 72.6605271,25.520404 72.4911534,26.3478788 C72.3424597,27.1215145 72.0497022,27.860316 71.6281231,28.5258182 C70.9908577,29.4891385 70.0844202,30.2438287 69.0215776,30.696 C67.7834618,31.2231176 66.4474127,31.48094 65.1020625,31.4523636 C64.1335045,31.4523045 63.1689244,31.3284732 62.2317594,31.0838788 C61.252533,30.8239772 60.3068642,30.4510466 59.41382,29.9726061 L58.9696988,26.7512727 L60.9575776,28.0506667 C61.5573127,28.4240193 62.2116549,28.7014604 62.8969716,28.8729697 C63.6457641,29.0836796 64.4192481,29.1938978 65.1970928,29.2007273 C66.7486079,29.2007273 67.9516786,28.8128485 68.8063049,28.0370909 C69.6609312,27.2613333 70.0863049,26.1571717 70.0824261,24.7246061 L70.0824261,23.582303 Z M70.0824261,21.5595152 L70.0824261,12.0739394 C69.4432737,11.7542576 68.7741844,11.4983147 68.0848503,11.3098182 C67.4816476,11.1457578 66.859767,11.0603633 66.2346685,11.0557576 C64.581012,11.0557576 63.2499413,11.6117172 62.2414564,12.7236364 C61.2329716,13.8355556 60.7280827,15.3146667 60.7267897,17.1609697 C60.7267897,18.9348687 61.179315,20.3428687 62.0843655,21.3849697 C62.989416,22.4270707 64.2086483,22.9474747 65.7420625,22.9461818 C66.3845516,22.9385817 67.0221469,22.8332967 67.6329716,22.6339394 C68.4792669,22.3480186 69.2988853,21.9885027 70.0824261,21.5595152 L70.0824261,21.5595152 Z"></path><path d="M75,9 L77.8276364,9 L82.2649697,20.6363636 L87.1483636,9 L89.4504242,9 L82.3813333,25.3238788 L82.2649697,25.5546667 C81.1478788,28.0965657 80.2874343,29.9066667 79.6836364,30.9849697 L76.7415758,30.9849697 C77.3683498,30.3346009 77.9302011,29.6246546 78.4191515,28.8652121 C79.0313917,27.8817808 79.5750659,26.8572886 80.046303,25.7990303 L81.0004848,23.7452121 L75,9 Z"></path></g></svg>
|
19
|
+
</header>
|
20
|
+
<main class="ErrorPage__main">
|
21
|
+
<div class="Alert Alert--danger">
|
22
|
+
<div class="Alert--danger__icon">
|
23
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"/></svg>
|
24
|
+
</div>
|
25
|
+
<div class="Alert__content">
|
26
|
+
<p class="Alert__title">Not authorized.</p>
|
27
|
+
<%
|
28
|
+
# TODO: Parameterize the below message to be app specific, via NulogySSO engine config.
|
29
|
+
%>
|
30
|
+
<p class="Alert__message">You do not have permission to access Nulogy.</p>
|
31
|
+
</div>
|
32
|
+
</div>
|
33
|
+
|
34
|
+
</main>
|
35
|
+
</div>
|
36
|
+
<div class="ErrorPage__logout" >
|
37
|
+
<%= link_to("Logout", nulogy_sso.logout_path, class: "Link") %>
|
38
|
+
</div>
|
39
|
+
</div>
|
40
|
+
<footer class="ErrorPage__footer">
|
41
|
+
© 2007-<%= Date.today.year %> Nulogy Corporation
|
42
|
+
</footer>
|
43
|
+
</div>
|
44
|
+
</body>
|
45
|
+
</html>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Inflections are used so that "SSO" can be used in the NulogySSO module name
|
4
|
+
# with this exact casing and still allow rails to evaluate directory paths as nulogy_sso
|
5
|
+
ActiveSupport::Inflector.inflections(:en) do |inflect|
|
6
|
+
inflect.acronym "SSO"
|
7
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NulogySSO
|
4
|
+
|
5
|
+
# A mix-in that is intended to enhance a controller with NulogySSO authentication code.
|
6
|
+
# It is recommended to `include NulogySSO::ControllerHelper` in your ApplicationController.
|
7
|
+
module ControllerHelper
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
# Makes the commonly used @current_user variable available to controllers and views.
|
12
|
+
# This emulates a code pattern popular in Rails apps using Devise.
|
13
|
+
attr_reader :current_user
|
14
|
+
helper_method :current_user
|
15
|
+
end
|
16
|
+
|
17
|
+
def authenticate_sso_user
|
18
|
+
raw_token = cookies[NulogySSO.auth_cookie_key]
|
19
|
+
return redirect_to nulogy_sso.login_path if raw_token.blank?
|
20
|
+
|
21
|
+
@current_user = Authenticator.new.authenticated_user(raw_token)
|
22
|
+
return redirect_to nulogy_sso.login_path if @current_user.blank?
|
23
|
+
return render status: :forbidden, template: "sso_error" unless valid_user?(@current_user)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def valid_user?(user)
|
29
|
+
NulogySSO.validate_user.call(user)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module NulogySSO
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace NulogySSO
|
4
|
+
|
5
|
+
# Load all gems in the gemspec, as opposed to explicit require calls
|
6
|
+
Bundler.require(*Rails.groups)
|
7
|
+
|
8
|
+
config.autoload_paths << File.expand_path("lib/nulogy_sso/controller_helper", __dir__)
|
9
|
+
|
10
|
+
# Instruct apps using Sprockets to include assets from NulogySSO
|
11
|
+
initializer "nulogy_sso.assets.precompile" do |app|
|
12
|
+
app.config.assets.precompile += ["nulogy_sso/sso_error.css", "nulogy_sso/favicon.png"]
|
13
|
+
end
|
14
|
+
|
15
|
+
config.after_initialize do
|
16
|
+
if NulogySSO.auth_config.blank?
|
17
|
+
raise "Missing auth_config config object. Consider using config_for() to load a YAML config file."
|
18
|
+
end
|
19
|
+
|
20
|
+
if NulogySSO.find_user_by_email.blank?
|
21
|
+
raise "Missing find_user_by_email config lambda."
|
22
|
+
end
|
23
|
+
|
24
|
+
if NulogySSO.validate_user.blank?
|
25
|
+
raise "Missing validate_user config lambda."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|