From 7cd9fa8b84dac949684ddfd417fee4e7b4a76c7c Mon Sep 17 00:00:00 2001 From: Stephen von Takach Date: Wed, 15 Mar 2023 19:51:24 +1100 Subject: [PATCH] Refactor/pg migration (#91) --- .gitattributes | 7 + .github/workflows/ci.yml | 11 +- .gitignore | 21 +- .ruby-version | 1 + Dockerfile | 7 +- Gemfile | 42 +- Gemfile.lock | 406 +++++++++--------- README.md | 42 +- Rakefile | 2 - app/controllers/auth/sessions_controller.rb | 2 +- app/helpers/user_helper.rb | 2 +- app/jobs/application_job.rb | 7 +- app/mailers/application_mailer.rb | 2 - app/models/adfs_strat.rb | 51 +-- app/models/application_record.rb | 107 +++++ app/models/authentication.rb | 27 +- app/models/authority.rb | 20 +- app/models/concerns/auth_timestamps.rb | 32 -- app/models/ldap_strat.rb | 22 +- app/models/oauth_strat.rb | 32 +- app/models/user.rb | 51 +-- app/views/layouts/mailer.html.erb | 13 + app/views/layouts/mailer.text.erb | 1 + config.ru | 3 +- config/application.rb | 45 +- config/boot.rb | 3 +- config/brakeman.ignore | 38 -- config/cable.yml | 10 + config/credentials.yml.enc | 1 + config/database.yml | 86 ++++ config/environment.rb | 2 - config/environments/development.rb | 52 ++- config/environments/production.rb | 52 ++- config/environments/test.rb | 47 +- config/initializers/backtrace_silencers.rb | 8 - config/initializers/cleanup_ttls.rb | 16 +- config/initializers/cors.rb | 17 - config/initializers/doorkeeper.rb | 23 +- config/initializers/inflections.rb | 9 +- config/initializers/logging.rb | 10 +- config/initializers/mime_types.rb | 5 - config/initializers/new_framework_defaults.rb | 14 - config/initializers/nobrainer.rb | 12 - config/initializers/opentelemetry.rb | 9 +- config/locales/en.yml | 16 +- config/puma.rb | 43 +- config/routes.rb | 2 + config/spring.rb | 8 - config/storage.yml | 34 ++ ...20221129033843_create_doorkeeper_tables.rb | 88 ++++ ...20221129034013_init.condo_active_record.rb | 29 ++ ...1129034014_filepath.condo_active_record.rb | 6 + ...1129034015_parallel.condo_active_record.rb | 8 + db/migrate/20221129044858_add_adfs_strat.rb | 42 ++ .../20221129044916_add_authentication.rb | 12 + db/migrate/20221129044926_add_authority.rb | 19 + db/migrate/20221129044939_add_ldap_strat.rb | 20 + db/migrate/20221129044951_add_oauth_strat.rb | 24 ++ db/migrate/20221129045002_add_user.rb | 44 ++ db/schema.rb | 195 +++++++++ db/seeds.rb | 20 +- docker-compose.yml | 49 +++ public/404.html | 67 +++ public/422.html | 67 +++ public/500.html | 66 +++ .../apple-touch-icon-precomposed.png | 0 .../.keep => public/apple-touch-icon.png | 0 test/mailers/.keep => public/favicon.ico | 0 public/robots.txt | 6 +- spec | 31 ++ storage/.keep | 0 test/application_system_test_case.rb | 5 + test/helpers/.keep | 0 test/system/.keep | 0 test/test_helper.rb | 16 +- tmp/pids/.keep | 0 tmp/storage/.keep | 0 vendor/.keep | 0 78 files changed, 1565 insertions(+), 722 deletions(-) create mode 100644 .gitattributes create mode 100644 .ruby-version create mode 100644 app/models/application_record.rb delete mode 100644 app/models/concerns/auth_timestamps.rb create mode 100644 app/views/layouts/mailer.html.erb create mode 100644 app/views/layouts/mailer.text.erb delete mode 100644 config/brakeman.ignore create mode 100644 config/cable.yml create mode 100644 config/credentials.yml.enc create mode 100644 config/database.yml delete mode 100644 config/initializers/backtrace_silencers.rb delete mode 100644 config/initializers/cors.rb delete mode 100644 config/initializers/mime_types.rb delete mode 100644 config/initializers/new_framework_defaults.rb delete mode 100644 config/initializers/nobrainer.rb delete mode 100644 config/spring.rb create mode 100644 config/storage.yml create mode 100644 db/migrate/20221129033843_create_doorkeeper_tables.rb create mode 100644 db/migrate/20221129034013_init.condo_active_record.rb create mode 100644 db/migrate/20221129034014_filepath.condo_active_record.rb create mode 100644 db/migrate/20221129034015_parallel.condo_active_record.rb create mode 100644 db/migrate/20221129044858_add_adfs_strat.rb create mode 100644 db/migrate/20221129044916_add_authentication.rb create mode 100644 db/migrate/20221129044926_add_authority.rb create mode 100644 db/migrate/20221129044939_add_ldap_strat.rb create mode 100644 db/migrate/20221129044951_add_oauth_strat.rb create mode 100644 db/migrate/20221129045002_add_user.rb create mode 100644 db/schema.rb create mode 100644 docker-compose.yml create mode 100644 public/404.html create mode 100644 public/422.html create mode 100644 public/500.html rename app/views/.keep => public/apple-touch-icon-precomposed.png (100%) rename test/fixtures/.keep => public/apple-touch-icon.png (100%) rename test/mailers/.keep => public/favicon.ico (100%) create mode 100644 spec create mode 100644 storage/.keep create mode 100644 test/application_system_test_case.rb create mode 100644 test/helpers/.keep create mode 100644 test/system/.keep create mode 100644 tmp/pids/.keep create mode 100644 tmp/storage/.keep create mode 100644 vendor/.keep diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..31eeee0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,7 @@ +# See https://git-scm.com/docs/gitattributes for more about git attribute files. + +# Mark the database schema as having been generated. +db/schema.rb linguist-generated + +# Mark any vendored files as having been vendored. +vendor/* linguist-vendored diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 069d2f5..1b0625b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ jobs: style: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Run StandardRB uses: andrewmcodes/standardrb-action@v0.0.2 env: @@ -13,8 +13,15 @@ jobs: security: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Run Brakeman Static Analysis uses: devmasx/brakeman-linter-action@v1.0.0 env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} + + tests: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run spec + run: bash ./spec diff --git a/.gitignore b/.gitignore index 80a621e..74b1054 100644 --- a/.gitignore +++ b/.gitignore @@ -10,11 +10,28 @@ # Ignore all logfiles and tempfiles. /log/* /tmp/* -/rethinkdb_data !/log/.keep !/tmp/.keep +# Ignore pidfiles, but keep the directory. +/tmp/pids/* +!/tmp/pids/ +!/tmp/pids/.keep + +# Ignore uploaded files in development. +/storage/* +!/storage/.keep +/tmp/storage/* +!/tmp/storage/ +!/tmp/storage/.keep + +/public/assets + +# Ignore master key for decrypting credentials and more. +/config/master.key + # Ignore Byebug command history file. .byebug_history *.DS_Store -config/master.key +config/master.key + diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..c877459 --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +ruby-3.1.3 diff --git a/Dockerfile b/Dockerfile index 425ccf2..dd7263d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ ARG RUBY_VER="3.1" FROM ruby:$RUBY_VER-alpine AS build-env -ARG PACKAGES="git libxml2 libxslt build-base curl-dev libxml2-dev libxslt-dev zlib-dev tzdata" +ARG PACKAGES="git libxml2 libxslt build-base curl-dev libxml2-dev libxslt-dev zlib-dev tzdata libpq-dev" RUN apk update && \ apk upgrade && \ @@ -43,7 +43,7 @@ WORKDIR $APP_DIR ENV BUNDLE_APP_CONFIG="$APP_DIR/.bundle" # install packages -ARG PACKAGES="tzdata libxml2 libxslt libc6-compat" +ARG PACKAGES="tzdata libxml2 libxslt libc6-compat libpq-dev" RUN apk update \ && apk upgrade \ && apk add --update --no-cache $PACKAGES @@ -57,6 +57,9 @@ ENV USER=appuser # See https://stackoverflow.com/a/55757473/12429735RUN RUN adduser -D -g "" -h "/nonexistent" -s "/sbin/nologin" -H -u "${UID}" "${USER}" RUN chown appuser:appuser -R /app/tmp + +# NOTE:: this should not be used, instead use `RAILS_MASTER_KEY` env var +RUN chown appuser:appuser -R /app/config/ RUN bundle binstubs bundler --force # Use an unprivileged user. diff --git a/Gemfile b/Gemfile index 911f089..9642b01 100644 --- a/Gemfile +++ b/Gemfile @@ -1,33 +1,34 @@ -# frozen_string_literal: true - source "https://rubygems.org" -gem "rails", "~> 6.0", ">= 6.0.5.1" +gem "rails", "~> 7.0.4" + # We don't use the mail gem gem "net-smtp", require: false gem "net-imap", require: false gem "net-pop", require: false +# Reduces boot times through caching; required in config/boot.rb +gem "bootsnap", require: false + # High performance web server gem "puma" # Database -gem "nobrainer", git: "https://github.com/place-labs/nobrainer.git", branch: "fix/ensure-fix-table-duplicate" +gem "pg" gem "redis" # Authentication -gem "doorkeeper", "~> 5.4" +gem "doorkeeper", "~> 5.6" gem "doorkeeper-jwt" -gem "doorkeeper-rethinkdb", git: "https://github.com/place-labs/doorkeeper-rethinkdb.git" gem "jwt" gem "omniauth", "~> 1.9" gem "omniauth-ldap2" gem "omniauth-oauth2" gem "omniauth-saml" -# Uploads -gem "condo", git: "https://github.com/cotag/Condominios.git", branch: "rethink-update" -gem "condo-rethinkdb", git: "https://github.com/place-labs/condo-rethinkdb.git" +# Uploads (rethink update looks like a rails compatibility update) +gem "condo", git: "https://github.com/cotag/Condominios.git", branch: "rails7" +gem "condo_active_record", git: "https://github.com/cotag/condo_active_record.git" # Model support gem "addressable" @@ -38,7 +39,7 @@ gem "email_validator" gem "lograge" gem "logstash-event" gem "mono_logger" -gem "sentry-raven" +gem "sentry-ruby" gem "opentelemetry-sdk" gem "opentelemetry-exporter-otlp" gem "opentelemetry-instrumentation-all" @@ -50,19 +51,22 @@ gem "rbtrace" gem "yajl-ruby" group :development, :test do - # Call 'byebug' anywhere in the code to stop execution and get a debugger console - gem "byebug", platform: :mri - gem "pry-rails", platform: :mri - gem "web-console", platform: :mri + # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem + gem "debug", platforms: %i[ mri mingw x64_mingw ] end group :development do - gem "listen", "~> 3.0" + # Use console on exceptions pages [https://github.com/rails/web-console] + gem "web-console" + gem "pry-rails" +end - # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring - # gem 'spring' - # gem 'spring-watcher-listen', '~> 2.0.0' +group :test do + # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] + gem "capybara" + gem "selenium-webdriver" + gem "webdrivers" end # Windows does not include zoneinfo files, so bundle the tzinfo-data gem -gem "tzinfo-data" # , platforms: [:mingw, :mswin, :x64_mingw, :jruby] +gem "tzinfo-data", platforms: %i[ mingw mswin x64_mingw jruby ] diff --git a/Gemfile.lock b/Gemfile.lock index bbf1f12..ea48619 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,10 +1,9 @@ GIT remote: https://github.com/cotag/Condominios.git - revision: afb1f1c51ab62b220be84205d18077dd87ff762f - branch: rethink-update + revision: 4ca8b5cfd19e49d91a47a2180dae8faa8348c857 + branch: rails7 specs: condo (2.1.0) - azure (~> 0.7.9) fog-aws fog-google fog-openstack @@ -12,129 +11,118 @@ GIT unf GIT - remote: https://github.com/place-labs/condo-rethinkdb.git - revision: 4f20f06e9b893da3ecbb2262157bbc6e08fa34e6 + remote: https://github.com/cotag/condo_active_record.git + revision: 2b63ba1e14133f14000523bf1302e3fc4fcbfd93 specs: - condo-rethinkdb (1.0.0) + condo_active_record (2.0.0) condo - nobrainer - rails (>= 4.0.0) - -GIT - remote: https://github.com/place-labs/doorkeeper-rethinkdb.git - revision: a750ca3d9dd90c466c0007b353a41f3120bc3678 - specs: - doorkeeper-rethinkdb (1.1.5) - doorkeeper (>= 4.0.0, < 6) - nobrainer (>= 0.33.0, < 1) - -GIT - remote: https://github.com/place-labs/nobrainer.git - revision: 151b051e4e66c830e083843e260fb7c558f8f08b - branch: fix/ensure-fix-table-duplicate - specs: - nobrainer (0.41.0) - activemodel (>= 4.1.0, < 8) - activesupport (>= 4.1.0, < 8) - middleware (~> 0.1.0) - rethinkdb (>= 2.3.0, < 2.5) - symbol_decoration (~> 1.1) + rails GEM remote: https://rubygems.org/ specs: - actioncable (6.0.6.1) - actionpack (= 6.0.6.1) + actioncable (7.0.4) + actionpack (= 7.0.4) + activesupport (= 7.0.4) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (6.0.6.1) - actionpack (= 6.0.6.1) - activejob (= 6.0.6.1) - activerecord (= 6.0.6.1) - activestorage (= 6.0.6.1) - activesupport (= 6.0.6.1) + actionmailbox (7.0.4) + actionpack (= 7.0.4) + activejob (= 7.0.4) + activerecord (= 7.0.4) + activestorage (= 7.0.4) + activesupport (= 7.0.4) mail (>= 2.7.1) - actionmailer (6.0.6.1) - actionpack (= 6.0.6.1) - actionview (= 6.0.6.1) - activejob (= 6.0.6.1) + net-imap + net-pop + net-smtp + actionmailer (7.0.4) + actionpack (= 7.0.4) + actionview (= 7.0.4) + activejob (= 7.0.4) + activesupport (= 7.0.4) mail (~> 2.5, >= 2.5.4) + net-imap + net-pop + net-smtp rails-dom-testing (~> 2.0) - actionpack (6.0.6.1) - actionview (= 6.0.6.1) - activesupport (= 6.0.6.1) - rack (~> 2.0, >= 2.0.8) + actionpack (7.0.4) + actionview (= 7.0.4) + activesupport (= 7.0.4) + rack (~> 2.0, >= 2.2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (6.0.6.1) - actionpack (= 6.0.6.1) - activerecord (= 6.0.6.1) - activestorage (= 6.0.6.1) - activesupport (= 6.0.6.1) + actiontext (7.0.4) + actionpack (= 7.0.4) + activerecord (= 7.0.4) + activestorage (= 7.0.4) + activesupport (= 7.0.4) + globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (6.0.6.1) - activesupport (= 6.0.6.1) + actionview (7.0.4) + activesupport (= 7.0.4) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (6.0.6.1) - activesupport (= 6.0.6.1) + activejob (7.0.4) + activesupport (= 7.0.4) globalid (>= 0.3.6) - activemodel (6.0.6.1) - activesupport (= 6.0.6.1) - activerecord (6.0.6.1) - activemodel (= 6.0.6.1) - activesupport (= 6.0.6.1) - activestorage (6.0.6.1) - actionpack (= 6.0.6.1) - activejob (= 6.0.6.1) - activerecord (= 6.0.6.1) + activemodel (7.0.4) + activesupport (= 7.0.4) + activerecord (7.0.4) + activemodel (= 7.0.4) + activesupport (= 7.0.4) + activestorage (7.0.4) + actionpack (= 7.0.4) + activejob (= 7.0.4) + activerecord (= 7.0.4) + activesupport (= 7.0.4) marcel (~> 1.0) - activesupport (6.0.6.1) + mini_mime (>= 1.1.0) + activesupport (7.0.4) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (>= 0.7, < 2) - minitest (~> 5.1) - tzinfo (~> 1.1) - zeitwerk (~> 2.2, >= 2.2.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) addressable (2.8.1) public_suffix (>= 2.0.2, < 6.0) - azure (0.7.10) - addressable (~> 2.3) - azure-core (~> 0.1) - faraday (~> 0.9) - faraday_middleware (~> 0.10) - mime-types (>= 1, < 4.0) - nokogiri (~> 1.6) - systemu (~> 2.6) - thor (~> 0.19) - azure-core (0.1.15) - faraday (~> 0.9) - faraday_middleware (~> 0.10) - nokogiri (~> 1.6) bcrypt (3.1.18) bindex (0.8.1) + bootsnap (1.15.0) + msgpack (~> 1.2) builder (3.2.4) - byebug (11.1.3) + capybara (3.38.0) + addressable + matrix + mini_mime (>= 0.1.3) + nokogiri (~> 1.8) + rack (>= 1.6.0) + rack-test (>= 0.6.3) + regexp_parser (>= 1.5, < 3.0) + xpath (~> 3.2) coderay (1.1.3) - concurrent-ruby (1.2.0) + concurrent-ruby (1.1.10) connection_pool (2.3.0) crass (1.0.6) - date (3.3.3) + date (3.3.1) + debug (1.7.0) + irb (>= 1.5.0) + reline (>= 0.3.1) declarative (0.0.20) - doorkeeper (5.6.4) + doorkeeper (5.6.2) railties (>= 5) doorkeeper-jwt (0.4.1) jwt (>= 2.1) email_validator (2.2.4) activemodel - erubi (1.12.0) - excon (0.99.0) - faraday (0.17.6) - multipart-post (>= 1.2, < 3) - faraday_middleware (0.14.0) - faraday (>= 0.7.4, < 1.0) + erubi (1.11.0) + excon (0.95.0) + faraday (2.7.2) + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-net_http (3.0.2) ffi (1.15.5) fog-aws (3.18.0) fog-core (~> 2.1) @@ -169,9 +157,9 @@ GEM formatador (0.3.0) globalid (1.1.0) activesupport (>= 5.0) - google-apis-compute_v1 (0.62.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-core (0.11.0) + google-apis-compute_v1 (0.56.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-core (0.9.2) addressable (~> 2.5, >= 2.5.1) googleauth (>= 0.16.2, < 2.a) httpclient (>= 2.8.1, < 3.a) @@ -180,23 +168,23 @@ GEM retriable (>= 2.0, < 4.a) rexml webrick - google-apis-dns_v1 (0.31.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-iamcredentials_v1 (0.17.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-monitoring_v3 (0.42.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-pubsub_v1 (0.34.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-sqladmin_v1beta4 (0.43.0) - google-apis-core (>= 0.11.0, < 2.a) - google-apis-storage_v1 (0.21.0) - google-apis-core (>= 0.11.0, < 2.a) + google-apis-dns_v1 (0.29.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-iamcredentials_v1 (0.16.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-monitoring_v3 (0.39.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-pubsub_v1 (0.31.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-sqladmin_v1beta4 (0.41.0) + google-apis-core (>= 0.9.1, < 2.a) + google-apis-storage_v1 (0.20.0) + google-apis-core (>= 0.9.1, < 2.a) google-cloud-env (1.6.0) faraday (>= 0.17.3, < 3.0) - google-protobuf (3.22.0) - google-protobuf (3.22.0-x86_64-linux) - googleapis-common-protos-types (1.5.0) + google-protobuf (3.21.12) + google-protobuf (3.21.12-x86_64-linux) + googleapis-common-protos-types (1.4.0) google-protobuf (~> 3.14) googleauth (1.3.0) faraday (>= 0.17.3, < 3.a) @@ -209,10 +197,10 @@ GEM httpclient (2.8.3) i18n (1.12.0) concurrent-ruby (~> 1.0) - jwt (2.7.0) - listen (3.8.0) - rb-fsevent (~> 0.10, >= 0.10.3) - rb-inotify (~> 0.9, >= 0.9.10) + io-console (0.5.11) + irb (1.6.1) + reline (>= 0.3.0) + jwt (2.5.0) lograge (0.12.0) actionpack (>= 4) activesupport (>= 4) @@ -222,27 +210,25 @@ GEM loofah (2.19.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) - mail (2.8.1) + mail (2.8.0) mini_mime (>= 0.1.1) net-imap net-pop net-smtp marcel (1.0.2) + matrix (0.4.2) memoist (0.16.2) method_source (1.0.0) - middleware (0.1.0) mime-types (3.4.1) mime-types-data (~> 3.2015) mime-types-data (3.2023.0218.1) mini_mime (1.1.2) - mini_portile2 (2.8.1) - minitest (5.17.0) + minitest (5.16.3) mono_logger (1.1.1) msgpack (1.6.0) multi_json (1.15.0) multi_xml (0.6.0) - multipart-post (2.3.0) - net-imap (0.3.4) + net-imap (0.3.2) date net-protocol net-ldap (0.17.1) @@ -253,12 +239,9 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.8) - nokogiri (1.14.2) - mini_portile2 (~> 2.8.0) + nokogiri (1.13.10-aarch64-linux) racc (~> 1.4) - nokogiri (1.14.2-aarch64-linux) - racc (~> 1.4) - nokogiri (1.14.2-x86_64-linux) + nokogiri (1.13.10-x86_64-linux) racc (~> 1.4) oauth2 (2.0.9) faraday (>= 0.17.3, < 3.0) @@ -291,11 +274,11 @@ GEM opentelemetry-common (~> 0.19.6) opentelemetry-sdk (~> 1.2) opentelemetry-semantic_conventions - opentelemetry-instrumentation-action_pack (0.5.0) + opentelemetry-instrumentation-action_pack (0.3.2) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) opentelemetry-instrumentation-rack (~> 0.21) - opentelemetry-instrumentation-action_view (0.4.0) + opentelemetry-instrumentation-action_view (0.3.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-active_support (~> 0.1) opentelemetry-instrumentation-base (~> 0.20) @@ -312,9 +295,10 @@ GEM opentelemetry-instrumentation-active_support (0.3.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-all (0.31.0) + opentelemetry-instrumentation-all (0.28.0) + opentelemetry-instrumentation-active_job (~> 0.3.0) opentelemetry-instrumentation-active_model_serializers (~> 0.19.0) - opentelemetry-instrumentation-aws_sdk (~> 0.3.0) + opentelemetry-instrumentation-aws_sdk (~> 0.3.1) opentelemetry-instrumentation-bunny (~> 0.19.0) opentelemetry-instrumentation-concurrent_ruby (~> 0.20.0) opentelemetry-instrumentation-dalli (~> 0.22.0) @@ -328,13 +312,13 @@ GEM opentelemetry-instrumentation-koala (~> 0.19.0) opentelemetry-instrumentation-lmdb (~> 0.21.0) opentelemetry-instrumentation-mongo (~> 0.21.0) - opentelemetry-instrumentation-mysql2 (~> 0.22.0) + opentelemetry-instrumentation-mysql2 (~> 0.21.1) opentelemetry-instrumentation-net_http (~> 0.21.0) - opentelemetry-instrumentation-pg (~> 0.23.0) + opentelemetry-instrumentation-pg (~> 0.22.1) opentelemetry-instrumentation-que (~> 0.5.0) opentelemetry-instrumentation-racecar (~> 0.1.0) - opentelemetry-instrumentation-rack (~> 0.22.0) - opentelemetry-instrumentation-rails (~> 0.25.0) + opentelemetry-instrumentation-rack (~> 0.21.1) + opentelemetry-instrumentation-rails (~> 0.23.1) opentelemetry-instrumentation-rake (~> 0.1.0) opentelemetry-instrumentation-rdkafka (~> 0.2.0) opentelemetry-instrumentation-redis (~> 0.24.0) @@ -356,7 +340,7 @@ GEM opentelemetry-instrumentation-concurrent_ruby (0.20.1) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-dalli (0.22.2) + opentelemetry-instrumentation-dalli (0.22.1) opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.21.0) @@ -392,45 +376,44 @@ GEM opentelemetry-instrumentation-lmdb (0.21.1) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-mongo (0.21.1) + opentelemetry-instrumentation-mongo (0.21.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-mysql2 (0.22.0) + opentelemetry-instrumentation-mysql2 (0.21.1) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-net_http (0.21.1) + opentelemetry-instrumentation-net_http (0.21.0) opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-pg (0.23.0) + opentelemetry-instrumentation-pg (0.22.3) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-que (0.5.1) + opentelemetry-instrumentation-que (0.5.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-racecar (0.1.1) + opentelemetry-instrumentation-racecar (0.1.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-rack (0.22.1) + opentelemetry-instrumentation-rack (0.21.1) opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-rails (0.25.0) + opentelemetry-instrumentation-rails (0.23.1) opentelemetry-api (~> 1.0) - opentelemetry-instrumentation-action_pack (~> 0.5.0) - opentelemetry-instrumentation-action_view (~> 0.4.0) - opentelemetry-instrumentation-active_job (~> 0.4.0) - opentelemetry-instrumentation-active_record (~> 0.5.0) - opentelemetry-instrumentation-active_support (~> 0.3.0) + opentelemetry-instrumentation-action_pack (~> 0.3.1) + opentelemetry-instrumentation-action_view (~> 0.3.0) + opentelemetry-instrumentation-active_record (~> 0.4.0) + opentelemetry-instrumentation-active_support (~> 0.2.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-rake (0.1.1) + opentelemetry-instrumentation-rake (0.1.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-rdkafka (0.2.2) + opentelemetry-instrumentation-rdkafka (0.2.1) opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-redis (0.24.1) + opentelemetry-instrumentation-redis (0.24.0) opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.21.0) @@ -448,12 +431,12 @@ GEM opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.21.0) - opentelemetry-instrumentation-sinatra (0.21.5) + opentelemetry-instrumentation-sinatra (0.21.1) opentelemetry-api (~> 1.0) opentelemetry-common (~> 0.19.3) opentelemetry-instrumentation-base (~> 0.21.0) opentelemetry-instrumentation-rack (~> 0.21) - opentelemetry-instrumentation-trilogy (0.51.1) + opentelemetry-instrumentation-trilogy (0.51.0) opentelemetry-api (~> 1.0) opentelemetry-instrumentation-base (~> 0.21.0) opentelemetry-semantic_conventions (>= 1.8.0) @@ -468,64 +451,64 @@ GEM opentelemetry-api (~> 1.0) optimist (3.0.1) os (1.1.4) - pry (0.14.2) + pg (1.4.5) + pry (0.14.1) coderay (~> 1.1) method_source (~> 1.0) pry-rails (0.3.9) pry (>= 0.10.4) public_suffix (5.0.1) - puma (6.1.0) + puma (6.0.0) nio4r (~> 2.0) pyu-ruby-sasl (0.0.3.3) - racc (1.6.2) - rack (2.2.6.2) + racc (1.6.1) + rack (2.2.4) rack-test (2.0.2) rack (>= 1.3) - rails (6.0.6.1) - actioncable (= 6.0.6.1) - actionmailbox (= 6.0.6.1) - actionmailer (= 6.0.6.1) - actionpack (= 6.0.6.1) - actiontext (= 6.0.6.1) - actionview (= 6.0.6.1) - activejob (= 6.0.6.1) - activemodel (= 6.0.6.1) - activerecord (= 6.0.6.1) - activestorage (= 6.0.6.1) - activesupport (= 6.0.6.1) - bundler (>= 1.3.0) - railties (= 6.0.6.1) - sprockets-rails (>= 2.0.0) + rails (7.0.4) + actioncable (= 7.0.4) + actionmailbox (= 7.0.4) + actionmailer (= 7.0.4) + actionpack (= 7.0.4) + actiontext (= 7.0.4) + actionview (= 7.0.4) + activejob (= 7.0.4) + activemodel (= 7.0.4) + activerecord (= 7.0.4) + activestorage (= 7.0.4) + activesupport (= 7.0.4) + bundler (>= 1.15.0) + railties (= 7.0.4) rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.5.0) + rails-html-sanitizer (1.4.4) loofah (~> 2.19, >= 2.19.1) - railties (6.0.6.1) - actionpack (= 6.0.6.1) - activesupport (= 6.0.6.1) + railties (7.0.4) + actionpack (= 7.0.4) + activesupport (= 7.0.4) method_source - rake (>= 0.8.7) - thor (>= 0.20.3, < 2.0) + rake (>= 12.2) + thor (~> 1.0) + zeitwerk (~> 2.5) rake (13.0.6) - rb-fsevent (0.11.2) - rb-inotify (0.10.1) - ffi (~> 1.0) rbtrace (0.4.14) ffi (>= 1.0.6) msgpack (>= 0.4.3) optimist (>= 3.0.0) - redis (5.0.6) + redis (5.0.5) redis-client (>= 0.9.0) - redis-client (0.12.2) + redis-client (0.11.2) connection_pool + regexp_parser (2.6.1) + reline (0.3.2) + io-console (~> 0.5) representable (3.2.0) declarative (< 0.1.0) trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) request_store (1.5.1) rack (>= 1.4) - rethinkdb (2.4.0.0) retriable (3.1.2) rexml (3.2.5) ruby-saml (1.15.0) @@ -533,8 +516,13 @@ GEM rexml ruby2_keywords (0.0.5) rubyntlm (0.6.3) - sentry-raven (2.13.0) - faraday (>= 0.7.6, < 1.0) + rubyzip (2.3.2) + selenium-webdriver (4.7.1) + rexml (~> 3.2, >= 3.2.5) + rubyzip (>= 1.2.2, < 3.0) + websocket (~> 1.0) + sentry-ruby (5.7.0) + concurrent-ruby (~> 1.0, >= 1.0.2) signet (0.17.0) addressable (~> 2.8) faraday (>= 0.17.5, < 3.a) @@ -543,23 +531,11 @@ GEM snaky_hash (2.0.1) hashie version_gem (~> 1.1, >= 1.1.1) - sprockets (4.2.0) - concurrent-ruby (~> 1.0) - rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) - sprockets (>= 3.0.0) - symbol_decoration (1.1.0) - systemu (2.6.5) - thor (0.20.3) - thread_safe (0.3.6) - timeout (0.3.2) + thor (1.2.1) + timeout (0.3.1) trailblazer-option (0.1.2) - tzinfo (1.2.11) - thread_safe (~> 0.1) - tzinfo-data (1.2022.7) - tzinfo (>= 1.0.0) + tzinfo (2.0.5) + concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext @@ -570,37 +546,42 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webrick (1.8.1) + webdrivers (5.2.0) + nokogiri (~> 1.6) + rubyzip (>= 1.3.0) + selenium-webdriver (~> 4.0) + webrick (1.7.0) + websocket (1.2.9) websocket-driver (0.7.5) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) + xpath (3.2.0) + nokogiri (~> 1.8) yajl-ruby (1.4.3) - zeitwerk (2.6.7) + zeitwerk (2.6.6) PLATFORMS - aarch64-linux-musl - ruby + aarch64-linux x86_64-linux DEPENDENCIES addressable bcrypt - byebug + bootsnap + capybara condo! - condo-rethinkdb! - doorkeeper (~> 5.4) + condo_active_record! + debug + doorkeeper (~> 5.6) doorkeeper-jwt - doorkeeper-rethinkdb! email_validator jwt - listen (~> 3.0) lograge logstash-event mono_logger net-imap net-pop net-smtp - nobrainer! omniauth (~> 1.9) omniauth-ldap2 omniauth-oauth2 @@ -608,14 +589,17 @@ DEPENDENCIES opentelemetry-exporter-otlp opentelemetry-instrumentation-all opentelemetry-sdk + pg pry-rails puma - rails (~> 6.0, >= 6.0.5.1) + rails (~> 7.0.4) rbtrace redis - sentry-raven + selenium-webdriver + sentry-ruby tzinfo-data web-console + webdrivers yajl-ruby BUNDLED WITH diff --git a/README.md b/README.md index 1ba5470..d61a1da 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## ENV VARS -``` +```bash # If a sentry Data Source Name is configured SENTRY_DSN=https://@.ingest.sentry.io/ @@ -46,7 +46,7 @@ Add these keys to the Authority internals config ### Amazon -``` +```yaml "storage": { "name": "AmazonS3", "access_id": "", @@ -57,7 +57,7 @@ Add these keys to the Authority internals config ### Google -``` +```yaml "storage": { "name": "GoogleCloudStorage", "access_id": "", @@ -66,21 +66,9 @@ Add these keys to the Authority internals config } ``` -### Microsoft Azure - -``` -"storage": { - "name": "MicrosoftAzure", - "account_name": "", - "access_key": "", - # optional defaults to {account_name}.blob.core.windows.net - "blob_host": nil -} -``` - ### OpenStack or RackSpace Cloud -``` +```yaml "storage": { "name": "OpenStackSwift", "username": "admin:admin", @@ -95,6 +83,19 @@ Add these keys to the Authority internals config } ``` +### Microsoft Azure + +currently disabled due to gem requirement clashes + +```yaml +"storage": { + "name": "MicrosoftAzure", + "account_name": "", + "access_key": "", + # optional defaults to {account_name}.blob.core.windows.net + "blob_host": nil +} +``` ## Authentication Flow @@ -106,7 +107,7 @@ Use the Client Credentials flow with body: -``` +```yaml { "grant_type" : "password", "username" : "user@example.com", @@ -116,7 +117,7 @@ with body: This will return -``` +```yaml { "access_token": "19adad999683f5b450c460726aa", "token_type": "bearer", @@ -124,7 +125,6 @@ This will return } ``` - ### Native App Auth 1. Login @@ -137,7 +137,7 @@ This will return This will return - ``` + ```yaml { "access_token": "de6780bc506a0446309bd9362820ba8aed28aa506c71eedbe1c5c4f9dd350e54", "token_type": "Bearer", @@ -148,7 +148,7 @@ This will return ## Revoking Refresh Tokens -``` +```text POST /auth/oauth/revoke?client_id=&client_secret= token= ``` diff --git a/Rakefile b/Rakefile index d2a78aa..9a5ea73 100644 --- a/Rakefile +++ b/Rakefile @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # Add your own tasks in files placed in lib/tasks ending in .rake, # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 3f58aa8..0ebd87a 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -131,7 +131,7 @@ def create else begin # Log-in the user currently authenticating - user = User.find?(auth_model.user_id) + user = User.find_by(id: auth_model.user_id) # There is no user model, so we want to recover from this automatically if user.nil? diff --git a/app/helpers/user_helper.rb b/app/helpers/user_helper.rb index 00e7bf2..fe03e7d 100644 --- a/app/helpers/user_helper.rb +++ b/app/helpers/user_helper.rb @@ -15,7 +15,7 @@ def current_user return nil unless user return remove_session if Time.now.to_i > user["expires"] - @current_user = User.find?(user["id"]) || remove_session + @current_user = User.find_by(id: user["id"]) || remove_session end def signed_in? diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb index d92ffdd..d394c3d 100644 --- a/app/jobs/application_job.rb +++ b/app/jobs/application_job.rb @@ -1,4 +1,7 @@ -# frozen_string_literal: true - class ApplicationJob < ActiveJob::Base + # Automatically retry jobs that encountered a deadlock + # retry_on ActiveRecord::Deadlocked + + # Most jobs are safe to ignore if the underlying records are no longer available + # discard_on ActiveJob::DeserializationError end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb index 5cc63a0..3c34c81 100644 --- a/app/mailers/application_mailer.rb +++ b/app/mailers/application_mailer.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - class ApplicationMailer < ActionMailer::Base default from: "from@example.com" layout "mailer" diff --git a/app/models/adfs_strat.rb b/app/models/adfs_strat.rb index c55998b..ceac6d2 100644 --- a/app/models/adfs_strat.rb +++ b/app/models/adfs_strat.rb @@ -1,51 +1,9 @@ -# frozen_string_literal: true +require_relative "application_record" -class AdfsStrat - include NoBrainer::Document - include AuthTimestamps +class AdfsStrat < ApplicationRecord + self.table_name = "adfs_strat" - table_config name: "adfs_strat" - - field :name, type: String - belongs_to :authority, index: true - - field :issuer, type: String, default: :aca - field :idp_sso_target_url_runtime_params, type: Hash - field :name_identifier_format, type: String, default: -> { "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" } - field :uid_attribute, type: String - - field :assertion_consumer_service_url, type: String - field :idp_sso_target_url, type: String - - field :idp_cert, type: String - field :idp_cert_fingerprint, type: String - - field :attribute_service_name, type: String - field :attribute_statements, type: Hash, default: lambda { - { - name: ["name"], - email: %w[email mail], - first_name: %w[first_name firstname firstName givenname], - last_name: %w[last_name lastname lastName surname] - } - } - field :request_attributes, type: Array, default: lambda { - [ - {name: "ImmutableID", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", - friendly_name: "Login Name"}, - {name: "email", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", - friendly_name: "Email address"}, - {name: "name", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", - friendly_name: "Full name"}, - {name: "first_name", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", - friendly_name: "Given name"}, - {name: "last_name", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", - friendly_name: "Family name"} - ] - } - - field :idp_slo_target_url, type: String - field :slo_default_relay_state, type: String + belongs_to :authority # Not actually sure what this type stuff is for? def type @@ -65,7 +23,6 @@ def serializable_hash(**options) validates :authority_id, presence: true validates :name, presence: true - validates :issuer, presence: true validates :idp_sso_target_url, presence: true validates :name_identifier_format, presence: true diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 0000000..c975718 --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,107 @@ +require 'socket' +require 'digest/md5' + +class ApplicationRecord < ActiveRecord::Base + primary_abstract_class + + class Retry < RuntimeError; end + + BASE_TABLE = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".freeze + + TIME_OFFSET = Time.utc(2014, 01, 01).to_i + + # 30 bits timestamp with 1s resolution -> We overflow in year 2048. Good enough. + # Math.log(Time.parse('2048-01-01').to_f - TIME_OFFSET)/Math.log(2) = 29.999 + TIMESTAMP_BITS = 30 + + # 14 bits of sequence number. max 16k values per 1s slices. + # We want something >10k because we want to be able to do high speed inserts + # on a single process for future benchmarks. + SEQUENCE_BITS = 14 + + # 24 bits of machine id + # 0.1% of chance to have a collision with 183 servers: + # Math.sqrt(-2*(2**24)*Math.log(0.999)) = 183.2 + # 1% of chance to have a collision with ~580 servers. + # When using more than 500 machines, it's therefore a good + # idea to set the machine_id manually to avoid collisions. + # XXX This is referenced in nobrainer/config.rb#default_machine_id + MACHINE_ID_BITS = 24 + + # 15 bits for the current pid. We wouldn't need it if the sequence number was + # on a piece of shared memory :) + PID_BITS = 15 + + # Total: 83 bits + # With 14 digits in [A-Za-z0-9], we can represent 83 bits: + # Math.log(62**14)/Math.log(2) = 83.35 + ID_STR_LENGTH = 14 + + TIMESTAMP_MASK = (1 << TIMESTAMP_BITS)-1 + SEQUENCE_MASK = (1 << SEQUENCE_BITS)-1 + MACHINE_ID_MASK = (1 << MACHINE_ID_BITS)-1 + PID_MASK = (1 << PID_BITS)-1 + + PID_SHIFT = 0 + MACHINE_ID_SHIFT = PID_SHIFT + PID_BITS + SEQUENCE_SHIFT = MACHINE_ID_SHIFT + MACHINE_ID_BITS + TIMESTAMP_SHIFT = SEQUENCE_SHIFT + SEQUENCE_BITS + + def self._default_machine_id + return ENV['MACHINE_ID'] if ENV['MACHINE_ID'] + + host = Socket.gethostname + if host.in? %w(127.0.0.1 localhost) + raise "Please configure ENV['MACHINE_ID'] due to lack of appropriate hostname (Socket.gethostname = #{host})" + end + + Digest::MD5.digest(host).unpack("N")[0] & MACHINE_ID_MASK + end + + @@machine_id = _default_machine_id + @@last_timestamp = 0 + @@sequence = @@first_sequence = rand(SEQUENCE_MASK/2) + + def self._generate + timestamp = (Time.now.to_i - TIME_OFFSET) & TIMESTAMP_MASK + + unless @@last_timestamp == timestamp + # more noise is better in the ID, but we prefer to avoid + # wrapping the sequences so that Model.last on a single + # machine returns the latest created document. + @@first_sequence = sequence = rand(SEQUENCE_MASK/2) + @@last_timestamp = timestamp + else + sequence = (@@sequence + 1) & SEQUENCE_MASK + raise Retry if @@first_sequence == sequence + end + @@sequence = sequence + + pid = Process.pid & PID_MASK + + (timestamp << TIMESTAMP_SHIFT) | (sequence << SEQUENCE_SHIFT) | + (@@machine_id << MACHINE_ID_SHIFT) | (pid << PID_SHIFT) + rescue Retry + sleep 0.1 + retry + end + + def self.convert_to_alphanum(id) + result = [] + until id.zero? + id, r = id.divmod(BASE_TABLE.size) + result << BASE_TABLE[r] + end + result.reverse.join.rjust(ID_STR_LENGTH, BASE_TABLE[0]) + end + + @@lock = Mutex.new + def self.generate + convert_to_alphanum(@@lock.synchronize { _generate }) + end + + before_create :generate_id + def generate_id + self.id = "#{self.class.table_name}-#{self.class.generate}" + end +end diff --git a/app/models/authentication.rb b/app/models/authentication.rb index 60f1c95..cb4d179 100644 --- a/app/models/authentication.rb +++ b/app/models/authentication.rb @@ -1,28 +1,17 @@ -# frozen_string_literal: true +require_relative "application_record" -class Authentication - include NoBrainer::Document - include AuthTimestamps +class Authentication < ApplicationRecord + self.table_name = "authentication" - table_config name: "authentication" + belongs_to :user + belongs_to :authority - field :uid, type: String - field :provider, type: String - - belongs_to :user, index: true - belongs_to :authority, index: true - - def self.by_user(user) - Authentication.where(user_id: user.id).to_a - end - - def self.for_user(user_id) - Authentication.where(user_id: user_id).to_a - end + scope :for_user, ->(user_id) { where(user_id: user_id) } + scope :by_user, ->(user_id) { where(user_id: user_id) } # Where auth is https://github.com/omniauth/omniauth/wiki/Auth-Hash-Schema def self.from_omniauth(authority_id, auth) - find?("auth-#{authority_id}-#{auth["provider"]}-#{auth["uid"]}") + find_by id: "auth-#{authority_id}-#{auth["provider"]}-#{auth["uid"]}" end def self.create_with_omniauth(authority_id, auth, user_id) diff --git a/app/models/authority.rb b/app/models/authority.rb index d02dd8a..de2a58f 100644 --- a/app/models/authority.rb +++ b/app/models/authority.rb @@ -1,20 +1,8 @@ -# frozen_string_literal: true - +require_relative "application_record" require "addressable/uri" -class Authority - include NoBrainer::Document - include AuthTimestamps - - table_config name: "authority" - - field :name, type: String - field :domain, type: String, uniq: true, index: true - field :description, type: String - field :login_url, type: String, default: "/login?continue={{url}}" - field :logout_url, type: String, default: "/auth/logout" - field :internals, type: Hash, default: -> { {} } - field :config, type: Hash, default: -> { {} } +class Authority < ApplicationRecord + self.table_name = "authority" validates :name, presence: true @@ -25,7 +13,7 @@ def domain=(dom) end def self.find_by_domain(name) - Authority.where(domain: name.downcase).first + find_by domain: name.downcase end def as_json(options = {}) diff --git a/app/models/concerns/auth_timestamps.rb b/app/models/concerns/auth_timestamps.rb deleted file mode 100644 index 652d7f2..0000000 --- a/app/models/concerns/auth_timestamps.rb +++ /dev/null @@ -1,32 +0,0 @@ -# frozen_string_literal: true - -require "active_support/all" - -module AuthTimestamps - extend ActiveSupport::Concern - - included do - field :created_at, type: Integer - field :updated_at, type: Integer - end - - def _create(options = {}) - now = Time.now.to_i - self.created_at = now unless created_at_changed? - self.updated_at = now unless updated_at_changed? - super - end - - def _update(attrs) - self.updated_at = Time.now.to_i unless updated_at_changed? - super(attrs.merge("updated_at" => @_attributes["updated_at"])) - end - - def cache_key - "#{super}-#{updated_at}" - end - - def touch - update!(updated_at: Time.now.to_i) - end -end diff --git a/app/models/ldap_strat.rb b/app/models/ldap_strat.rb index 50ff6bf..a54f126 100644 --- a/app/models/ldap_strat.rb +++ b/app/models/ldap_strat.rb @@ -1,23 +1,9 @@ -# frozen_string_literal: true +require_relative "application_record" -class LdapStrat - include NoBrainer::Document - include AuthTimestamps +class LdapStrat < ApplicationRecord + self.table_name = "ldap_strat" - table_config name: "ldap_strat" - - field :name, type: String # (used as title) - - belongs_to :authority, index: true - - field :port, type: Integer, default: 636 - field :auth_method, type: String, default: :ssl - field :uid, type: String, default: -> { "sAMAccountName" } - field :host, type: String - field :base, type: String - field :bind_dn, type: String - field :password, type: String # This should not be plain text - field :filter + belongs_to :authority def type "ldaps" diff --git a/app/models/oauth_strat.rb b/app/models/oauth_strat.rb index 928d917..dcf53b5 100644 --- a/app/models/oauth_strat.rb +++ b/app/models/oauth_strat.rb @@ -1,33 +1,9 @@ -# frozen_string_literal: true +require_relative "application_record" -class OauthStrat - include NoBrainer::Document - include AuthTimestamps +class OauthStrat < ApplicationRecord + self.table_name = "oauth_strat" - table_config name: "oauth_strat" - - field :name, type: String - - belongs_to :authority, index: true - - field :client_id, type: String - field :client_secret, type: String - - # Maps oauth fields to our fields - field :info_mappings, type: Hash - - # additional params to be sent as part of the authorisation request - field :authorize_params, type: Hash, default: -> { {} } - - # Security checks to be made on the returned data String => Array(String) - field :ensure_matching, type: Hash, default: -> { {} } - field :site, type: String - field :authorize_url, type: String - field :token_method, type: String - field :auth_scheme, type: String - field :token_url, type: String - field :scope, type: String - field :raw_info_url, type: String + belongs_to :authority def type "oauths" diff --git a/app/models/user.rb b/app/models/user.rb index c6801e4..17868f4 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,15 +1,12 @@ -# frozen_string_literal: true - +require_relative "application_record" require "email_validator" require "digest/md5" require "bcrypt" -class User - include NoBrainer::Document - include AuthTimestamps +class User < ApplicationRecord include BCrypt - table_config name: "user" + self.table_name = "user" PUBLIC_DATA = {only: %i[ id email_digest nickname name first_name last_name groups @@ -17,52 +14,16 @@ class User department preferred_language staff_id ]}.freeze - field :id, type: String, primary_key: true, default: -> { "user-#{::NoBrainer::Document::PrimaryKey::Generator.generate}" } - field :name, type: String - field :nickname, type: String - field :email, type: String, index: true - field :phone, type: String - field :country, type: String - field :image, type: String - field :ui_theme, type: String - field :misc, type: String - - field :login_name, type: String, index: true - field :staff_id, type: String, index: true - field :first_name, type: String - field :last_name, type: String - field :building, type: String - field :department, type: String - field :preferred_language, type: String - - field :password_digest, type: String - field :email_digest, type: String, index: true - - field :card_number, type: String - field :deleted, type: Boolean, default: false - - # typically LDAP groups - field :groups, type: Array, default: -> { [] } - - # User credentials - field :access_token, type: String - field :refresh_token, type: String - field :expires_at, type: Integer - field :expires, type: Boolean - - belongs_to :authority, index: true + belongs_to :authority has_many :authentications, dependent: :destroy has_many :access_tokens, class_name: "Doorkeeper::AccessToken", dependent: :destroy, foreign_key: :resource_owner_id has_many :access_grants, class_name: "Doorkeeper::AccessGrant", dependent: :destroy, foreign_key: :resource_owner_id def self.find_by_email(authority, email) email_digest = Digest::MD5.hexdigest(email.downcase) - User.where(authority_id: authority, email_digest: email_digest).first + find_by authority_id: authority, email_digest: email_digest end - field :sys_admin, default: false, index: true - field :support, default: false - before_save :build_name, if: ->(model) { model.first_name.present? } def build_name self.name = "#{first_name} #{last_name}" @@ -113,13 +74,13 @@ def password=(new_password) # END PASSWORD METHODS # Make reference to the email= function of the model - alias_method :assign_email, :email= def email=(new_email) super(new_email) # For looking up user pictures without making the email public self.email_digest = new_email ? Digest::MD5.hexdigest(new_email.downcase) : nil end + alias_method :assign_email, :email= # Validations validates :email, email: true diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 0000000..cbd34d2 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 0000000..37f0bdd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/config.ru b/config.ru index bff88d6..4a3c09a 100644 --- a/config.ru +++ b/config.ru @@ -1,7 +1,6 @@ -# frozen_string_literal: true - # This file is used by Rack-based servers to start the application. require_relative "config/environment" run Rails.application +Rails.application.load_server diff --git a/config/application.rb b/config/application.rb index 21ed822..5f1d40e 100644 --- a/config/application.rb +++ b/config/application.rb @@ -1,23 +1,6 @@ -# frozen_string_literal: true - require_relative "boot" -require "rails" -# Pick the frameworks you want: -require "active_model/railtie" -require "active_job/railtie" -# require "active_record/railtie" -require "action_controller/railtie" -require "action_mailer/railtie" -require "action_view/railtie" -# require "action_cable/engine" -# require "sprockets/railtie" -require "rails/test_unit/railtie" - -require "nobrainer" -require_relative "../app/models/concerns/auth_timestamps" -require_relative "../app/models/authority" -require_relative "../app/models/user" +require "rails/all" require_relative "../app/middleware/rewrite_callback_request" require_relative "../app/middleware/rewrite_redirect_response" @@ -26,13 +9,10 @@ # you've limited to :test, :development, or :production. Bundler.require(*Rails.groups) -module EngineApp +module PlaceosAuth class Application < Rails::Application - config.load_defaults "6.0" - - # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. + # Initialize configuration defaults for originally generated Rails version. + config.load_defaults 7.0 # Only loads a smaller set of middleware suitable for API only apps. # Middleware like session, flash, cookies can be added back manually. @@ -40,26 +20,11 @@ class Application < Rails::Application config.api_only = false config.action_dispatch.use_cookies_with_metadata = false - # Selectively switch between API and full rails stacks - config.before_initialize do |app| - # Authentication stack is different - # app.config.middleware.insert_after Rack::Runtime, ::ActionDispatch::Flash - # app.config.middleware.insert_after Rack::Runtime, ::OmniAuth::Builder, &OmniAuthConfig - # app.config.middleware.insert_after Rack::Runtime, ::Rails.application.config.session_options - # app.config.middleware.insert_after Rack::Runtime, ::ActionDispatch::Cookies - end - - Rails.application.config.middleware.insert_before 0, RewriteCallbackRequest - Rails.application.config.middleware.insert_before 0, RewriteRedirectResponse - + # Fix 404 routing for logging config.after_initialize do |app| - # Fix 404 routing for logging app.routes.append do match "*any", via: :all, to: "errors#not_found" end - - # Ensure indeses are synced - NoBrainer.sync_indexes end end end diff --git a/config/boot.rb b/config/boot.rb index c2241d7..988a5dd 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,5 +1,4 @@ -# frozen_string_literal: true - ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) require "bundler/setup" # Set up gems listed in the Gemfile. +require "bootsnap/setup" # Speed up boot time by caching expensive operations. diff --git a/config/brakeman.ignore b/config/brakeman.ignore deleted file mode 100644 index bbae8aa..0000000 --- a/config/brakeman.ignore +++ /dev/null @@ -1,38 +0,0 @@ -{ - "ignored_warnings": [ - { - "warning_type": "Redirect", - "warning_code": 18, - "fingerprint": "3e3db19f2c8fefa95c3c84536464648163031f05772053248447b795f2e2bb4f", - "check_name": "Redirect", - "message": "Possible unprotected redirect", - "file": "config/initializers/doorkeeper.rb", - "line": 27, - "link": "https://brakemanscanner.org/docs/warning_types/redirect/", - "code": "redirect_to(Authority.find_by_domain(request.host).login_url.gsub(\"{{url}}\", URI.encode_www_form_component(request.original_fullpath)))", - "render_path": null, - "location": null, - "user_input": "Authority.find_by_domain(request.host).login_url.gsub(\"{{url}}\", URI.encode_www_form_component(request.original_fullpath))", - "confidence": "High", - "note": "Redirects to original request via user-defined domain" - }, - { - "warning_type": "Redirect", - "warning_code": 18, - "fingerprint": "3e3db19f2c8fefa95c3c84536464648163031f05772053248447b795f2e2bb4f", - "check_name": "Redirect", - "message": "Possible unprotected redirect", - "file": "config/initializers/doorkeeper.rb", - "line": 37, - "link": "https://brakemanscanner.org/docs/warning_types/redirect/", - "code": "redirect_to(Authority.find_by_domain(request.host).login_url.gsub(\"{{url}}\", URI.encode_www_form_component(request.original_fullpath)))", - "render_path": null, - "location": null, - "user_input": "Authority.find_by_domain(request.host).login_url.gsub(\"{{url}}\", URI.encode_www_form_component(request.original_fullpath))", - "confidence": "High", - "note": "Redirects to original request via user-defined domain" - } - ], - "updated": "2021-12-02 12:47:06 +1100", - "brakeman_version": "5.1.2" -} diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 0000000..5dab7ae --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,10 @@ +development: + adapter: async + +test: + adapter: test + +production: + adapter: redis + url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> + channel_prefix: placeos_auth_production diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc new file mode 100644 index 0000000..7406d13 --- /dev/null +++ b/config/credentials.yml.enc @@ -0,0 +1 @@ +U/Zf6NS1OVbqQZdZk2JaCb5MzBLYGOJ6RWnv+thPnkgn1ZQnxRfhIWozAHFA92/pYn9OX0WW9J/1dwA4QzkgPrIPu+Ym3N4AsKhhTCx7WcEVsH+o/l8Pu+O2f4kArMP+cyJ6ShUtT+n13J1IunUeCLwpv1ExVTi+Gd8VzGkZJY7HMVw+yMgQlykuzB6OkbYvSo6XiDk/9F18zNL9FPf1ggqbKIYy7VZGc/yky/rZQ1NajuQdOKPV/3Bs13DZ5wtmSeGGt0jh8A6Pz//SxSbYpq0=--EQpAIvGAe2IfvrWR--pHFRZSjPazZpNmBbsnEwlg== \ No newline at end of file diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 0000000..4d82229 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,86 @@ +# PostgreSQL. Versions 9.3 and up are supported. +# +# Install the pg driver: +# gem install pg +# On macOS with Homebrew: +# gem install pg -- --with-pg-config=/usr/local/bin/pg_config +# On macOS with MacPorts: +# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config +# On Windows: +# gem install pg +# Choose the win32 build. +# Install PostgreSQL and put its /bin directory on your path. +# +# Configure Using Gemfile +# gem "pg" +# +default: &default + adapter: postgresql + encoding: unicode + # For details on connection pooling, see Rails configuration guide + # https://guides.rubyonrails.org/configuring.html#database-pooling + pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> + database: <%= ENV["PG_DB"] %> + username: <%= ENV["PG_USER"] %> + password: <%= ENV["PG_PASSWORD"] %> + host: <%= ENV["PG_HOST"] %> + port: <%= ENV["PG_PORT"] %> + +development: + <<: *default + + # The specified database role being used to connect to postgres. + # To create additional roles in postgres see `$ createuser --help`. + # When left blank, postgres will use the default role. This is + # the same name as the operating system user running Rails. + #username: placeos_auth + + # The password associated with the postgres role (username). + #password: + + # Connect on a TCP socket. Omitted by default since the client uses a + # domain socket that doesn't need configuration. Windows does not have + # domain sockets, so uncomment these lines. + #host: localhost + + # The TCP port the server listens on. Defaults to 5432. + # If your server runs on a different port number, change accordingly. + #port: 5432 + + # Schema search path. The server defaults to $user,public + #schema_search_path: myapp,sharedapp,public + + # Minimum log levels, in increasing order: + # debug5, debug4, debug3, debug2, debug1, + # log, notice, warning, error, fatal, and panic + # Defaults to warning. + #min_messages: notice + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + +# As with config/credentials.yml, you never want to store sensitive information, +# like your database password, in your source code. If your source code is +# ever seen by anyone, they now have access to your database. +# +# Instead, provide the password or a full connection URL as an environment +# variable when you boot the app. For example: +# +# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" +# +# If the connection URL is provided in the special DATABASE_URL environment +# variable, Rails will automatically merge its configuration values on top of +# the values provided in this file. Alternatively, you can specify a connection +# URL environment variable explicitly: +# +# production: +# url: <%= ENV["MY_APP_DATABASE_URL"] %> +# +# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database +# for a full overview on how database connection configuration can be specified. +# +production: + <<: *default diff --git a/config/environment.rb b/config/environment.rb index 7df99e8..cac5315 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # Load the Rails application. require_relative "application" diff --git a/config/environments/development.rb b/config/environments/development.rb index ea55a64..a629dfb 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,29 +1,34 @@ -# frozen_string_literal: true +require "active_support/core_ext/integer/time" Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # In development only, allow any hostname (instead of just localhost and IPs) - config.hosts.clear - - # In the development environment your application's code is reloaded on - # every request. This slows down response time but is perfect for development + # In the development environment your application's code is reloaded any time + # it changes. This slows down response time but is perfect for development # since you don't have to restart the web server when you make code changes. config.cache_classes = false + # disable host blocking + config.hosts.clear + # Do not eager load code on boot. config.eager_load = false # Show full error reports. config.consider_all_requests_local = true + # Enable server timing + config.server_timing = true + # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. if Rails.root.join("tmp/caching-dev.txt").exist? config.action_controller.perform_caching = true + config.action_controller.enable_fragment_cache_logging = true config.cache_store = :memory_store config.public_file_server.headers = { - "Cache-Control" => "public, max-age=172800" + "Cache-Control" => "public, max-age=#{2.days.to_i}" } else config.action_controller.perform_caching = false @@ -31,6 +36,9 @@ config.cache_store = :null_store end + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false @@ -39,13 +47,27 @@ # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true - logger = ActiveSupport::Logger.new($stdout) - logger.formatter = config.log_formatter - config.logger = ActiveSupport::TaggedLogging.new(logger) + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raise an error on page load if there are pending migrations. + config.active_record.migration_error = false + + # Highlight code that triggered database queries in logs. + config.active_record.verbose_query_logs = true + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Uncomment if you wish to allow Action Cable access from any origin. + # config.action_cable.disable_request_forgery_protection = true - # Use an evented file watcher to asynchronously detect changes in source code, - # routes, locales, etc. This feature depends on the listen gem. - config.file_watcher = ActiveSupport::EventedFileUpdateChecker + # Enable stdout logger + config.logger = Logger.new(STDOUT) end diff --git a/config/environments/production.rb b/config/environments/production.rb index cc380fb..f41a001 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -1,4 +1,4 @@ -# frozen_string_literal: true +require "active_support/core_ext/integer/time" # Remove the locks from the logger require "mono_logger" @@ -34,6 +34,11 @@ def initialize(*targets) Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. + # disable host blocking + config.hosts.clear + + config.active_record.migration_error = false + # Code is not reloaded between requests. config.cache_classes = true @@ -44,36 +49,55 @@ def initialize(*targets) config.eager_load = true # Full error reports are disabled and caching is turned on. - config.consider_all_requests_local = false + config.consider_all_requests_local = false config.action_controller.perform_caching = true + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. config.public_file_server.enabled = ENV["RAILS_SERVE_STATIC_FILES"].present? + # Compress CSS using a preprocessor. + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + # config.assets.compile = false + # Enable serving of images, stylesheets, and JavaScripts from an asset server. - # config.action_controller.asset_host = 'http://assets.example.com' + # config.asset_host = "http://assets.example.com" # Specifies the header that your server uses for sending files. - # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache - # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX + # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache + # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX + + # Store uploaded files on the local file system (see config/storage.yml for options). + config.active_storage.service = :local + + # Mount Action Cable outside main process or domain. + # config.action_cable.mount_path = nil + # config.action_cable.url = "wss://example.com/cable" + # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. # config.force_ssl = true - # Use the lowest log level to ensure availability of diagnostic information - # when problems arise. + # Include generic and useful information about system operation, but avoid logging too much + # information to avoid inadvertent exposure of personally identifiable information (PII). config.log_level = :info # Prepend all log lines with the following tags. - config.log_tags = [:request_id] + config.log_tags = [ :request_id ] # Use a different cache store in production. # config.cache_store = :mem_cache_store - # Use a real queuing backend for Active Job (and separate queues per environment) + # Use a real queuing backend for Active Job (and separate queues per environment). # config.active_job.queue_adapter = :resque - # config.active_job.queue_name_prefix = "place-app_#{Rails.env}" + # config.active_job.queue_name_prefix = "placeos_auth_production" + config.action_mailer.perform_caching = false # Ignore bad email addresses and do not raise email delivery errors. @@ -84,12 +108,14 @@ def initialize(*targets) # the I18n.default_locale when a translation cannot be found). config.i18n.fallbacks = true - # Send deprecation notices to registered listeners. - config.active_support.deprecation = :notify + # Don't log any deprecations. + config.active_support.report_deprecations = false # Use default logging formatter so that PID and timestamp are not suppressed. config.log_formatter = ::Logger::Formatter.new - config.action_mailer.raise_delivery_errors = true + + # Do not dump schema after migrations. + config.active_record.dump_schema_after_migration = false # Custom logging $stdout.sync = true diff --git a/config/environments/test.rb b/config/environments/test.rb index c45ffca..aa178fe 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -1,34 +1,43 @@ -# frozen_string_literal: true +require "active_support/core_ext/integer/time" + +# The test environment is used exclusively to run your application's +# test suite. You never need to work with it otherwise. Remember that +# your test database is "scratch space" for the test suite and is wiped +# and recreated between test runs. Don't rely on the data there! Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - # The test environment is used exclusively to run your application's - # test suite. You never need to work with it otherwise. Remember that - # your test database is "scratch space" for the test suite and is wiped - # and recreated between test runs. Don't rely on the data there! + config.active_record.migration_error = false + + # Turn false under Spring and add config.action_view.cache_template_loading = true. config.cache_classes = true - # Do not eager load code on boot. This avoids loading your whole application - # just for the purpose of running a single test. If you are using a tool that - # preloads Rails for running tests, you may have to set it to true. - config.eager_load = false + # Eager loading loads your whole application. When running a single test locally, + # this probably isn't necessary. It's a good idea to do in a continuous integration + # system, or in some way before deploying your code. + config.eager_load = ENV["CI"].present? # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true config.public_file_server.headers = { - "Cache-Control" => "public, max-age=3600" + "Cache-Control" => "public, max-age=#{1.hour.to_i}" } # Show full error reports and disable caching. - config.consider_all_requests_local = true + config.consider_all_requests_local = true config.action_controller.perform_caching = false + config.cache_store = :null_store # Raise exceptions instead of rendering exception templates. config.action_dispatch.show_exceptions = false # Disable request forgery protection in test environment. config.action_controller.allow_forgery_protection = false + + # Store uploaded files on the local file system in a temporary directory. + config.active_storage.service = :test + config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. @@ -39,6 +48,18 @@ # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr - # Raises error for missing translations - # config.action_view.raise_on_missing_translations = true + # Raise exceptions for disallowed deprecations. + config.active_support.disallowed_deprecation = :raise + + # Tell Active Support which deprecation messages to disallow. + config.active_support.disallowed_deprecation_warnings = [] + + # Raises error for missing translations. + # config.i18n.raise_on_missing_translations = true + + # Annotate rendered view with file names. + # config.action_view.annotate_rendered_view_with_filenames = true + + # Enable stdout logger + config.logger = Logger.new(STDOUT) end diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb deleted file mode 100644 index d0f0d3b..0000000 --- a/config/initializers/backtrace_silencers.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. -# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } - -# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. -# Rails.backtrace_cleaner.remove_silencers! diff --git a/config/initializers/cleanup_ttls.rb b/config/initializers/cleanup_ttls.rb index cca073c..edac3b3 100644 --- a/config/initializers/cleanup_ttls.rb +++ b/config/initializers/cleanup_ttls.rb @@ -9,13 +9,15 @@ begin expired = 10.minutes.ago.to_i - NoBrainer.run(db: "rethinkdb") do |r| - r.table("table_config").filter { |sys| sys["indexes"].contains("ttl") } - end.map { |table| table["name"] }.each do |table| - NoBrainer.run do |r| - r.table(table).between(past, expired, index: "ttl").delete - end - end + # TODO:: need to implement this for PostgreSQL, probably should live in a different service + + #NoBrainer.run(db: "rethinkdb") do |r| + # r.table("table_config").filter { |sys| sys["indexes"].contains("ttl") } + #end.map { |table| table["name"] }.each do |table| + # NoBrainer.run do |r| + # r.table(table).between(past, expired, index: "ttl").delete + # end + #end rescue => e puts "error clearing expired ttl: #{e.message}" end diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb deleted file mode 100644 index 5f68d44..0000000 --- a/config/initializers/cors.rb +++ /dev/null @@ -1,17 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# Avoid CORS issues when API is called from the frontend app. -# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. - -# Read more: https://github.com/cyu/rack-cors - -# Rails.application.config.middleware.insert_before 0, Rack::Cors do -# allow do -# origins 'example.com' -# -# resource '*', -# headers: :any, -# methods: [:get, :post, :put, :patch, :delete, :options, :head] -# end -# end diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 8a4c37b..c702d60 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -6,10 +6,8 @@ require "securerandom" require "doorkeeper" require "doorkeeper/jwt" -require "doorkeeper-rethinkdb" Doorkeeper.configure do - orm :rethinkdb hash_token_secrets # This block will be called to check whether the @@ -19,7 +17,7 @@ # the cookie to particular paths (i.e. /auth) cookie = cookies.encrypted[:user] - user = User.find?(cookie["id"]) if cookie && Time.now.to_i < cookie["expires"] + user = User.find_by(id: cookie["id"]) if cookie && Time.now.to_i < cookie["expires"] unless user current_authority = Authority.find_by_domain(request.host) login_url = current_authority.login_url @@ -53,15 +51,9 @@ end end - # Skip authorization only if the app is owned by us - if Rails.env.production? - skip_authorization do |_resource_owner, client| - client.application.skip_authorization - end - else - skip_authorization do |_resource_owner, _client| - true - end + # Skip authorization as we've not implemented it + skip_authorization do + true end # username and password authentication for local auth @@ -166,8 +158,7 @@ # Set the encryption secret. This would be shared with any other applications # that should be able to read the payload of the token. Defaults to "secret". - key = ENV["JWT_SECRET"] - key = key.try { |k| Base64.decode64(k) } || <<~KEY + DEV_KEY = <<~KEY -----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAt01C9NBQrA6Y7wyIZtsyur191SwSL3MjR58RIjZ5SEbSyzMG 3r9v12qka4UtpB2FmON2vwn0fl/7i3Jgh1Xth/s+TqgYXMebdd123wodrbex5pi3 @@ -196,8 +187,12 @@ 4n455vizig2c4/sxU5yu9AF9Dv+qNsGCx2e9uUOTDUlHM9NXwxU9rQ== -----END RSA PRIVATE KEY----- KEY + key = ENV["JWT_SECRET"] + key = key.try { |k| Base64.decode64(k) } || DEV_KEY secret_key key + puts "WARN: insecure development secret in use" if key == DEV_KEY + # Specify encryption type (https://github.com/progrium/ruby-jwt) encryption_method :rs256 end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index aa7435f..3860f65 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -1,17 +1,16 @@ -# frozen_string_literal: true # Be sure to restart your server when you modify this file. # Add new inflection rules using the following format. Inflections # are locale specific, and you may define rules for as many different # locales as you wish. All of these examples are active by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.plural /^(ox)$/i, '\1en' -# inflect.singular /^(ox)en/i, '\1' -# inflect.irregular 'person', 'people' +# inflect.plural /^(ox)$/i, "\\1en" +# inflect.singular /^(ox)en/i, "\\1" +# inflect.irregular "person", "people" # inflect.uncountable %w( fish sheep ) # end # These inflection rules are supported but not enabled by default: # ActiveSupport::Inflector.inflections(:en) do |inflect| -# inflect.acronym 'RESTful' +# inflect.acronym "RESTful" # end diff --git a/config/initializers/logging.rb b/config/initializers/logging.rb index 8608a93..aa5cc05 100644 --- a/config/initializers/logging.rb +++ b/config/initializers/logging.rb @@ -1,9 +1,11 @@ -# frozen_string_literal: true - # Be sure to restart your server when you modify this file. -# Configure sensitive parameters which will be filtered from the log file. -Rails.application.config.filter_parameters += [:password] +# Configure parameters to be filtered from the log file. Use this to limit dissemination of +# sensitive information. See the ActiveSupport::ParameterFilter documentation for supported +# notations and behaviors. +Rails.application.config.filter_parameters += [ + :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn, :password +] # Sentry DSN looks like: 'http://f0e5cf1431f24936ba99b7dc2bbc1af0:ac94cdf93bfb480ea45dd8889c97a817@sentry:8989/1' # Note replace the sentry host with the container DNS "sentry" diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb deleted file mode 100644 index 6e1d16f..0000000 --- a/config/initializers/mime_types.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true -# Be sure to restart your server when you modify this file. - -# Add new mime types for use in respond_to blocks: -# Mime::Type.register "text/richtext", :rtf diff --git a/config/initializers/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb deleted file mode 100644 index e474d0e..0000000 --- a/config/initializers/new_framework_defaults.rb +++ /dev/null @@ -1,14 +0,0 @@ -# frozen_string_literal: true - -# Be sure to restart your server when you modify this file. -# -# This file contains migration options to ease your Rails 5.0 upgrade. -# -# Read the Rails 5.0 release notes for more info on each option. - -# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. -# Previous versions had false. -ActiveSupport.to_time_preserves_timezone = true - -# Configure SSL options to enable HSTS with subdomains. Previous versions had false. -Rails.application.config.ssl_options = {hsts: {subdomains: true}} diff --git a/config/initializers/nobrainer.rb b/config/initializers/nobrainer.rb deleted file mode 100644 index f02861d..0000000 --- a/config/initializers/nobrainer.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -# Here is the place to configure NoBrainer. -# We are using the default connection settings. -NoBrainer.configure do |config| - # config.rethinkdb_url = ENV['RDB_URL'] || "rethinkdb://localhost/#{Rails.app.name}_#{Rails.env}" - - # 64kb - config.max_string_length = 65_536 -end - -# NoBrainer.sync_indexes diff --git a/config/initializers/opentelemetry.rb b/config/initializers/opentelemetry.rb index ddb8080..9088d76 100644 --- a/config/initializers/opentelemetry.rb +++ b/config/initializers/opentelemetry.rb @@ -2,7 +2,10 @@ require "opentelemetry/exporter/otlp" require "opentelemetry/instrumentation/all" -OpenTelemetry::SDK.configure do |c| - c.service_name = "auth" - c.use_all # enables all instrumentation! +endpoint = ENV["OTEL_EXPORTER_OTLP_ENDPOINT"] +if endpoint + OpenTelemetry::SDK.configure do |c| + c.service_name = "auth" + c.use_all # enables all instrumentation! + end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 0653957..8ca56fc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -4,11 +4,11 @@ # # To use the locales, use `I18n.t`: # -# I18n.t 'hello' +# I18n.t "hello" # # In views, this is aliased to just `t`: # -# <%= t('hello') %> +# <%= t("hello") %> # # To use a different locale, set it with `I18n.locale`: # @@ -16,8 +16,18 @@ # # This would use the information in config/locales/es.yml. # +# The following keys must be escaped otherwise they will not be retrieved by +# the default I18n backend: +# +# true, false, on, off, yes, no +# +# Instead, surround them with single quotes. +# +# en: +# "true": "foo" +# # To learn more, please read the Rails Internationalization guide -# available at http://guides.rubyonrails.org/i18n.html. +# available at https://guides.rubyonrails.org/i18n.html. en: hello: "Hello world" diff --git a/config/puma.rb b/config/puma.rb index 3d8fe69..a068510 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -1,13 +1,42 @@ -# frozen_string_literal: true - -threads_count = ENV.fetch("RAILS_WORKER_THREADS", 32).to_i +# Puma can serve each request in a thread from an internal thread pool. +# The `threads` method setting takes two numbers: a minimum and maximum. +# Any libraries that use thread pools should be configured to match +# the maximum value specified for Puma. Default is set to 5 threads for minimum +# and maximum; this matches the default thread size of Active Record. +# +threads_count = ENV.fetch("RAILS_WORKER_THREADS", 24).to_i threads threads_count, threads_count +# Specifies the `worker_timeout` threshold that Puma will use to wait before +# terminating a worker in development environments. +# +worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" + +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +# +port ENV.fetch("PORT") { 3000 } + +# Specifies the `environment` that Puma will run in. +# +environment ENV.fetch("RAILS_ENV") { "development" } + +# Specifies the `pidfile` that Puma will use. +pidfile ENV.fetch("PIDFILE") { "/app/tmp/puma.pid" } + +# Specifies the number of `workers` to boot in clustered mode. +# Workers are forked web server processes. If using threads and workers together +# the concurrency of the application would be max `threads` * `workers`. +# Workers do not work on JRuby or Windows (both of which do not support +# processes). worker_count = ENV.fetch("RAILS_WORKER_COUNT", 4).to_i workers worker_count -# We need to prevent these threads from launching until after the fork if we are -# going to use preload_app (forking is dangerous) -# [1] ! # - /app/config/initializers/cleanup_ttls.rb:8:in `sleep' -# [1] ! # - /app/vendor/bundle/ruby/2.7.0/gems/rethinkdb-2.4.0.0/lib/net.rb:1016:in `read' +# Use the `preload_app!` method when specifying a `workers` number. +# This directive tells Puma to first boot the application and load code +# before forking the application. This takes advantage of Copy On Write +# process behavior so workers use less memory. +# # preload_app! + +# Allow puma to be restarted by `bin/rails restart` command. +plugin :tmp_restart diff --git a/config/routes.rb b/config/routes.rb index 6168b08..0cea82a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ Rails.application.routes.draw do + # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html + resources :test scope :auth do diff --git a/config/spring.rb b/config/spring.rb deleted file mode 100644 index c5933e4..0000000 --- a/config/spring.rb +++ /dev/null @@ -1,8 +0,0 @@ -# frozen_string_literal: true - -%w[ - .ruby-version - .rbenv-vars - tmp/restart.txt - tmp/caching-dev.txt -].each { |path| Spring.watch(path) } diff --git a/config/storage.yml b/config/storage.yml new file mode 100644 index 0000000..4942ab6 --- /dev/null +++ b/config/storage.yml @@ -0,0 +1,34 @@ +test: + service: Disk + root: <%= Rails.root.join("tmp/storage") %> + +local: + service: Disk + root: <%= Rails.root.join("storage") %> + +# Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) +# amazon: +# service: S3 +# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> +# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> +# region: us-east-1 +# bucket: your_own_bucket-<%= Rails.env %> + +# Remember not to checkin your GCS keyfile to a repository +# google: +# service: GCS +# project: your_project +# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> +# bucket: your_own_bucket-<%= Rails.env %> + +# Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) +# microsoft: +# service: AzureStorage +# storage_account_name: your_account_name +# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> +# container: your_container_name-<%= Rails.env %> + +# mirror: +# service: Mirror +# primary: local +# mirrors: [ amazon, google, microsoft ] diff --git a/db/migrate/20221129033843_create_doorkeeper_tables.rb b/db/migrate/20221129033843_create_doorkeeper_tables.rb new file mode 100644 index 0000000..6edf48a --- /dev/null +++ b/db/migrate/20221129033843_create_doorkeeper_tables.rb @@ -0,0 +1,88 @@ +# frozen_string_literal: true + +class CreateDoorkeeperTables < ActiveRecord::Migration[7.0] + def change + create_table :oauth_applications do |t| + t.string :name, null: false + t.string :uid, null: false + t.string :secret, null: false + + # Remove `null: false` if you are planning to use grant flows + # that doesn't require redirect URI to be used during authorization + # like Client Credentials flow or Resource Owner Password. + t.text :redirect_uri, null: false + t.string :scopes, null: false, default: '' + t.boolean :confidential, null: false, default: true + t.timestamps null: false + end + + add_index :oauth_applications, :uid, unique: true + + create_table :oauth_access_grants do |t| + t.references :resource_owner, null: false + t.references :application, null: false + t.string :token, null: false + t.integer :expires_in, null: false + t.text :redirect_uri, null: false + t.datetime :created_at, null: false + t.datetime :revoked_at + t.string :scopes, null: false, default: '' + end + + add_index :oauth_access_grants, :token, unique: true + add_foreign_key( + :oauth_access_grants, + :oauth_applications, + column: :application_id + ) + + create_table :oauth_access_tokens do |t| + t.references :resource_owner, index: true + + # Remove `null: false` if you are planning to use Password + # Credentials Grant flow that doesn't require an application. + t.references :application, null: false + + # If you use a custom token generator you may need to change this column + # from string to text, so that it accepts tokens larger than 255 + # characters. More info on custom token generators in: + # https://github.com/doorkeeper-gem/doorkeeper/tree/v3.0.0.rc1#custom-access-token-generator + # + # t.text :token, null: false + t.string :token, null: false + + t.string :refresh_token + t.integer :expires_in + t.datetime :revoked_at + t.datetime :created_at, null: false + t.string :scopes + + # The authorization server MAY issue a new refresh token, in which case + # *the client MUST discard the old refresh token* and replace it with the + # new refresh token. The authorization server MAY revoke the old + # refresh token after issuing a new refresh token to the client. + # @see https://datatracker.ietf.org/doc/html/rfc6749#section-6 + # + # Doorkeeper implementation: if there is a `previous_refresh_token` column, + # refresh tokens will be revoked after a related access token is used. + # If there is no `previous_refresh_token` column, previous tokens are + # revoked as soon as a new access token is created. + # + # Comment out this line if you want refresh tokens to be instantly + # revoked after use. + t.string :previous_refresh_token, null: false, default: "" + end + + add_index :oauth_access_tokens, :token, unique: true + add_index :oauth_access_tokens, :refresh_token, unique: true + add_foreign_key( + :oauth_access_tokens, + :oauth_applications, + column: :application_id + ) + + # Uncomment below to ensure a valid reference to the resource owner's table + # add_foreign_key :oauth_access_grants, , column: :resource_owner_id + # add_foreign_key :oauth_access_tokens, , column: :resource_owner_id + end +end diff --git a/db/migrate/20221129034013_init.condo_active_record.rb b/db/migrate/20221129034013_init.condo_active_record.rb new file mode 100644 index 0000000..600c45c --- /dev/null +++ b/db/migrate/20221129034013_init.condo_active_record.rb @@ -0,0 +1,29 @@ +# This migration comes from condo_active_record (originally 20111001022500) +class Init < ActiveRecord::Migration[7.0] + def change + # + # Create the table for storing the uploads currently being processed + # + create_table :condo_uploads do |t| + t.string :user_id, :allow_null => false + + t.string :file_name, :allow_null => false + t.integer :file_size, :allow_null => false + t.string :file_id + t.text :custom_params + + t.string :provider_namespace + t.string :provider_name, :allow_null => false + t.string :provider_location, :allow_null => false + + t.string :bucket_name, :allow_null => false + t.string :object_key, :allow_null => false + t.text :object_options + + t.string :resumable_id + t.boolean :resumable, :allow_null => false, :default => false + + t.timestamps # date_created needs to be defined + end + end +end diff --git a/db/migrate/20221129034014_filepath.condo_active_record.rb b/db/migrate/20221129034014_filepath.condo_active_record.rb new file mode 100644 index 0000000..b37f691 --- /dev/null +++ b/db/migrate/20221129034014_filepath.condo_active_record.rb @@ -0,0 +1,6 @@ +# This migration comes from condo_active_record (originally 20111106022500) +class Filepath < ActiveRecord::Migration[7.0] + def change + add_column :condo_uploads, :file_path, :text + end +end diff --git a/db/migrate/20221129034015_parallel.condo_active_record.rb b/db/migrate/20221129034015_parallel.condo_active_record.rb new file mode 100644 index 0000000..9cb4090 --- /dev/null +++ b/db/migrate/20221129034015_parallel.condo_active_record.rb @@ -0,0 +1,8 @@ +# This migration comes from condo_active_record (originally 20160214022500) +class Parallel < ActiveRecord::Migration[7.0] + def change + add_column :condo_uploads, :part_list, :string + add_column :condo_uploads, :part_data, :text + remove_column :condo_uploads, :custom_params + end +end diff --git a/db/migrate/20221129044858_add_adfs_strat.rb b/db/migrate/20221129044858_add_adfs_strat.rb new file mode 100644 index 0000000..8270f2c --- /dev/null +++ b/db/migrate/20221129044858_add_adfs_strat.rb @@ -0,0 +1,42 @@ +class AddAdfsStrat < ActiveRecord::Migration[7.0] + def change + create_table :adfs_strat, id: :string do |t| + t.timestamps + + t.string :name + t.string :issuer + t.json :idp_sso_target_url_runtime_params + t.string :name_identifier_format, default: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + t.string :uid_attribute + t.string :assertion_consumer_service_url + t.string :idp_sso_target_url + t.string :idp_cert + t.string :idp_cert_fingerprint + t.string :attribute_service_name + t.json(:attribute_statements, default: { + name: ["name"], + email: %w[email mail], + first_name: %w[first_name firstname firstName givenname], + last_name: %w[last_name lastname lastName surname] + }) + t.json(:request_attributes, default: [ + {name: "ImmutableID", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + friendly_name: "Login Name"}, + {name: "email", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + friendly_name: "Email address"}, + {name: "name", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + friendly_name: "Full name"}, + {name: "first_name", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + friendly_name: "Given name"}, + {name: "last_name", name_format: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", + friendly_name: "Family name"} + ]) + t.string :issuidp_slo_target_urler + t.string :slo_default_relay_state + t.string :authority_id + end + + # add this in the authority migration + # add_foreign_key(:articles, :authors, if_not_exists: true) + end +end diff --git a/db/migrate/20221129044916_add_authentication.rb b/db/migrate/20221129044916_add_authentication.rb new file mode 100644 index 0000000..ce2a8c8 --- /dev/null +++ b/db/migrate/20221129044916_add_authentication.rb @@ -0,0 +1,12 @@ +class AddAuthentication < ActiveRecord::Migration[7.0] + def change + create_table :authentication, id: :string do |t| + t.timestamps + + t.string :uid, null: false + t.string :provider, null: false + t.string :user_id + t.string :authority_id + end + end +end diff --git a/db/migrate/20221129044926_add_authority.rb b/db/migrate/20221129044926_add_authority.rb new file mode 100644 index 0000000..365b3bc --- /dev/null +++ b/db/migrate/20221129044926_add_authority.rb @@ -0,0 +1,19 @@ +class AddAuthority < ActiveRecord::Migration[7.0] + def change + create_table :authority, id: :string do |t| + t.timestamps + + t.string :name, null: false + t.string :description + t.string :domain, null: false + t.string :login_url + t.string :logout_url + + t.json :internals + t.json :config + end + + add_foreign_key(:adfs_strat, :authority, if_not_exists: true) + add_foreign_key(:authentication, :authority, if_not_exists: true) + end +end diff --git a/db/migrate/20221129044939_add_ldap_strat.rb b/db/migrate/20221129044939_add_ldap_strat.rb new file mode 100644 index 0000000..cc91e2a --- /dev/null +++ b/db/migrate/20221129044939_add_ldap_strat.rb @@ -0,0 +1,20 @@ +class AddLdapStrat < ActiveRecord::Migration[7.0] + def change + create_table :ldap_strat, id: :string do |t| + t.timestamps + + t.string :name + t.integer :port + t.string :auth_method + t.string :uid + t.string :host + t.string :base + t.string :bind_dn + t.string :password + t.string :filter + t.string :authority_id + end + + add_foreign_key(:ldap_strat, :authority, if_not_exists: true) + end +end diff --git a/db/migrate/20221129044951_add_oauth_strat.rb b/db/migrate/20221129044951_add_oauth_strat.rb new file mode 100644 index 0000000..b07c0c6 --- /dev/null +++ b/db/migrate/20221129044951_add_oauth_strat.rb @@ -0,0 +1,24 @@ +class AddOauthStrat < ActiveRecord::Migration[7.0] + def change + create_table :oauth_strat, id: :string do |t| + t.timestamps + + t.string :name + t.string :client_id + t.string :client_secret + t.json :info_mappings + t.json :authorize_params + t.json :ensure_matching + t.string :site + t.string :authorize_url + t.string :token_method + t.string :auth_scheme + t.string :token_url + t.string :scope + t.string :raw_info_url + t.string :authority_id + end + + add_foreign_key(:oauth_strat, :authority, if_not_exists: true) + end +end diff --git a/db/migrate/20221129045002_add_user.rb b/db/migrate/20221129045002_add_user.rb new file mode 100644 index 0000000..40263a7 --- /dev/null +++ b/db/migrate/20221129045002_add_user.rb @@ -0,0 +1,44 @@ +class AddUser < ActiveRecord::Migration[7.0] + def change + create_table :user, id: :string do |t| + t.timestamps + + t.string :name + t.string :nickname + t.string :client_secret + t.string :email + t.string :phone + t.string :country + t.string :image + t.string :ui_theme + t.string :misc + t.string :login_name + t.string :staff_id + t.string :first_name + t.string :last_name + t.string :building + t.string :department + t.string :preferred_language + t.string :password_digest + t.string :email_digest + t.string :card_number + t.string :groups, array: true + + t.boolean :deleted + + t.string :access_token + t.string :refresh_token + t.bigint :expires_at + t.boolean :expires + + t.string :password + + t.boolean :sys_admin + t.boolean :support + + t.string :authority_id + end + + add_foreign_key(:user, :authority, if_not_exists: true) + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..08dcb07 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,195 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[7.0].define(version: 2022_11_29_045002) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + + create_table "adfs_strat", id: :string, force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name" + t.string "issuer" + t.json "idp_sso_target_url_runtime_params" + t.string "name_identifier_format", default: "urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified" + t.string "uid_attribute" + t.string "assertion_consumer_service_url" + t.string "idp_sso_target_url" + t.string "idp_cert" + t.string "idp_cert_fingerprint" + t.string "attribute_service_name" + t.json "attribute_statements", default: {"name"=>["name"], "email"=>["email", "mail"], "first_name"=>["first_name", "firstname", "firstName", "givenname"], "last_name"=>["last_name", "lastname", "lastName", "surname"]} + t.json "request_attributes", default: [{"name"=>"ImmutableID", "name_format"=>"urn:oasis:names:tc:SAML:2.0:attrname-format:basic", "friendly_name"=>"Login Name"}, {"name"=>"email", "name_format"=>"urn:oasis:names:tc:SAML:2.0:attrname-format:basic", "friendly_name"=>"Email address"}, {"name"=>"name", "name_format"=>"urn:oasis:names:tc:SAML:2.0:attrname-format:basic", "friendly_name"=>"Full name"}, {"name"=>"first_name", "name_format"=>"urn:oasis:names:tc:SAML:2.0:attrname-format:basic", "friendly_name"=>"Given name"}, {"name"=>"last_name", "name_format"=>"urn:oasis:names:tc:SAML:2.0:attrname-format:basic", "friendly_name"=>"Family name"}] + t.string "issuidp_slo_target_urler" + t.string "slo_default_relay_state" + t.string "authority_id" + end + + create_table "authentication", id: :string, force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "uid", null: false + t.string "provider", null: false + t.string "user_id" + t.string "authority_id" + end + + create_table "authority", id: :string, force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name", null: false + t.string "description" + t.string "domain", null: false + t.string "login_url" + t.string "logout_url" + t.json "internals" + t.json "config" + end + + create_table "condo_uploads", force: :cascade do |t| + t.string "user_id" + t.string "file_name" + t.integer "file_size" + t.string "file_id" + t.string "provider_namespace" + t.string "provider_name" + t.string "provider_location" + t.string "bucket_name" + t.string "object_key" + t.text "object_options" + t.string "resumable_id" + t.boolean "resumable", default: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "file_path" + t.string "part_list" + t.text "part_data" + end + + create_table "ldap_strat", id: :string, force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name" + t.integer "port" + t.string "auth_method" + t.string "uid" + t.string "host" + t.string "base" + t.string "bind_dn" + t.string "password" + t.string "filter" + t.string "authority_id" + end + + create_table "oauth_access_grants", force: :cascade do |t| + t.bigint "resource_owner_id", null: false + t.bigint "application_id", null: false + t.string "token", null: false + t.integer "expires_in", null: false + t.text "redirect_uri", null: false + t.datetime "created_at", null: false + t.datetime "revoked_at" + t.string "scopes", default: "", null: false + t.index ["application_id"], name: "index_oauth_access_grants_on_application_id" + t.index ["resource_owner_id"], name: "index_oauth_access_grants_on_resource_owner_id" + t.index ["token"], name: "index_oauth_access_grants_on_token", unique: true + end + + create_table "oauth_access_tokens", force: :cascade do |t| + t.bigint "resource_owner_id" + t.bigint "application_id", null: false + t.string "token", null: false + t.string "refresh_token" + t.integer "expires_in" + t.datetime "revoked_at" + t.datetime "created_at", null: false + t.string "scopes" + t.string "previous_refresh_token", default: "", null: false + t.index ["application_id"], name: "index_oauth_access_tokens_on_application_id" + t.index ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true + t.index ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id" + t.index ["token"], name: "index_oauth_access_tokens_on_token", unique: true + end + + create_table "oauth_applications", force: :cascade do |t| + t.string "name", null: false + t.string "uid", null: false + t.string "secret", null: false + t.text "redirect_uri", null: false + t.string "scopes", default: "", null: false + t.boolean "confidential", default: true, null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true + end + + create_table "oauth_strat", id: :string, force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name" + t.string "client_id" + t.string "client_secret" + t.json "info_mappings" + t.json "authorize_params" + t.json "ensure_matching" + t.string "site" + t.string "authorize_url" + t.string "token_method" + t.string "auth_scheme" + t.string "token_url" + t.string "scope" + t.string "raw_info_url" + t.string "authority_id" + end + + create_table "user", id: :string, force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name" + t.string "nickname" + t.string "client_secret" + t.string "email" + t.string "phone" + t.string "country" + t.string "image" + t.string "ui_theme" + t.string "misc" + t.string "login_name" + t.string "staff_id" + t.string "first_name" + t.string "last_name" + t.string "building" + t.string "department" + t.string "preferred_language" + t.string "password_digest" + t.string "email_digest" + t.string "card_number" + t.string "groups", array: true + t.boolean "deleted" + t.string "access_token" + t.string "refresh_token" + t.bigint "expires_at" + t.boolean "expires" + t.string "password" + t.boolean "sys_admin" + t.boolean "support" + t.string "authority_id" + end + + add_foreign_key "adfs_strat", "authority" + add_foreign_key "authentication", "authority" + add_foreign_key "ldap_strat", "authority" + add_foreign_key "oauth_access_grants", "oauth_applications", column: "application_id" + add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id" + add_foreign_key "oauth_strat", "authority" + add_foreign_key "user", "authority" +end diff --git a/db/seeds.rb b/db/seeds.rb index 8744e3c..988e435 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,8 +1,12 @@ -# frozen_string_literal: true -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). -# -# Examples: -# -# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) -# Character.create(name: 'Luke', movie: movies.first) + +Authority.destroy_all + +Authority.create!([ + { + name: "localhost Domain", + domain: "http://localhost/", + description: "used for testing" + } +]) + +puts "Created #{Authority.count} Authority" diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..ef26386 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,49 @@ +version: "3.7" + +x-postgresdb-client-env: &postgresdb-client-env + PG_HOST: ${PG_HOST:-postgres} + PG_PORT: ${PG_PORT:-5432} + PG_DB: ${PG_DB:-place_development} + PG_USER: ${PG_USER:-postgres} + PG_PASSWORD: ${PG_PASSWORD:-password} + +services: + postgres: + hostname: postgres + image: postgres + healthcheck: + test: ["CMD-SHELL", "pg_isready -U postgres"] + interval: 30s + timeout: 30s + retries: 3 + environment: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: password + POSTGRES_DB: place_development + healthcheck: + test: /usr/bin/pg_isready + interval: 5s + ports: + - 5432:5432 + + # docker-compose exec -it auth2 rake db:create db:migrate + # docker-compose exec -it auth2 bundle exec rails test + auth2: + build: . + user: root + ports: + - "3000:8080" + hostname: auth + volumes: + - ./db:/app/db + - ./app:/app/app + - ./test:/app/test + depends_on: + - postgres + environment: + COAUTH_NO_SSL: "true" + TZ: $TZ + PLACE_URI: https://localhost:8443 + RAILS_ENV: ${RAILS_ENV:-test} + SECRET_KEY_BASE: ${SECRET_KEY_BASE:-7d46ace87401c657323a47690dbe4a4c126e6de5a8de49fa47e92404fd80ba29eaa09f6910fc1ceb8ebb53234670b1870c558dfa3719671c98cf84b195cfc462} + <<: *postgresdb-client-env diff --git a/public/404.html b/public/404.html new file mode 100644 index 0000000..2be3af2 --- /dev/null +++ b/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/422.html b/public/422.html new file mode 100644 index 0000000..c08eac0 --- /dev/null +++ b/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/500.html b/public/500.html new file mode 100644 index 0000000..78a030a --- /dev/null +++ b/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/app/views/.keep b/public/apple-touch-icon-precomposed.png similarity index 100% rename from app/views/.keep rename to public/apple-touch-icon-precomposed.png diff --git a/test/fixtures/.keep b/public/apple-touch-icon.png similarity index 100% rename from test/fixtures/.keep rename to public/apple-touch-icon.png diff --git a/test/mailers/.keep b/public/favicon.ico similarity index 100% rename from test/mailers/.keep rename to public/favicon.ico diff --git a/public/robots.txt b/public/robots.txt index 3c9c7c0..c19f78a 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -1,5 +1 @@ -# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file -# -# To ban all spiders from the entire site uncomment the next two lines: -# User-agent: * -# Disallow: / +# See https://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file diff --git a/spec b/spec new file mode 100644 index 0000000..d253ec6 --- /dev/null +++ b/spec @@ -0,0 +1,31 @@ +#! /usr/bin/env bash + +set -eu + +docker-compose down + +# this function is called when Ctrl-C is sent +function trap_ctrlc () +{ + docker-compose down &> /dev/null + exit 2 +} + +# initialise trap to call trap_ctrlc function +# when signal 2 (SIGINT) is received +trap "trap_ctrlc" 2 + +docker-compose pull -q +docker-compose build -q +# docker-compose up -d + +exit_code="0" + +# docker-compose exec -it auth2 rake db:create db:migrate +# docker-compose exec -it auth2 bundle exec rails db:seed +# docker-compose exec -it auth2 bundle exec rails test + +docker-compose run -it --entrypoint="" auth2 bundle exec rails test +docker-compose down &> /dev/null + +exit ${exit_code} diff --git a/storage/.keep b/storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/application_system_test_case.rb b/test/application_system_test_case.rb new file mode 100644 index 0000000..d19212a --- /dev/null +++ b/test/application_system_test_case.rb @@ -0,0 +1,5 @@ +require "test_helper" + +class ApplicationSystemTestCase < ActionDispatch::SystemTestCase + driven_by :selenium, using: :chrome, screen_size: [1400, 1400] +end diff --git a/test/helpers/.keep b/test/helpers/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/system/.keep b/test/system/.keep new file mode 100644 index 0000000..e69de29 diff --git a/test/test_helper.rb b/test/test_helper.rb index 9a79774..d713e37 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,11 +1,13 @@ -# frozen_string_literal: true - ENV["RAILS_ENV"] ||= "test" -require File.expand_path("../config/environment", __dir__) +require_relative "../config/environment" require "rails/test_help" -module ActiveSupport - class TestCase - # Add more helper methods to be used by all tests here... - end +class ActiveSupport::TestCase + # Run tests in parallel with specified workers + parallelize(workers: :number_of_processors) + + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + # Add more helper methods to be used by all tests here... end diff --git a/tmp/pids/.keep b/tmp/pids/.keep new file mode 100644 index 0000000..e69de29 diff --git a/tmp/storage/.keep b/tmp/storage/.keep new file mode 100644 index 0000000..e69de29 diff --git a/vendor/.keep b/vendor/.keep new file mode 100644 index 0000000..e69de29