From 889a8e7cca9a456c17893a92e9925a7a155a8643 Mon Sep 17 00:00:00 2001 From: Epitacio Neto Date: Tue, 10 Dec 2024 14:17:12 +1100 Subject: [PATCH 1/4] CORS security patch fix on files docker-compose.yml and config/application.rb --- config/application.rb | 15 ++++++++++++--- docker-compose.yml | 6 ++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/config/application.rb b/config/application.rb index df21df01b..7eb1aa3d1 100644 --- a/config/application.rb +++ b/config/application.rb @@ -177,12 +177,21 @@ class Application < Rails::Application Rails.root.join('app', 'models', 'similarity') # CORS config - config.middleware.insert_before Warden::Manager, Rack::Cors do + # config.middleware.insert_before Warden::Manager, Rack::Cors do + # allow do + # origins '*' + # resource '*', headers: :any, methods: %i(get post put delete options) + # end + # end + + # Updated CORS Security Patch Fix + config.middleware.insert_before Warden::Manager, Rack::Cors do allow do - origins '*' + origins ENV['DF_ALLOWED_ORIGINS'].split(',') resource '*', headers: :any, methods: %i(get post put delete options) end - end + end + # Generators for test framework if Rails.env.test? config.generators do |g| diff --git a/docker-compose.yml b/docker-compose.yml index 22520ccaa..e00ccd416 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,6 +14,12 @@ services: environment: RAILS_ENV: 'development' + # CORS Vulnerability Remediation + # The DF_ALLOWED_ORIGINS variable must reflect the exact URLs where the OnTrack app will be accessed (e.g., production, staging, or development URLs). + # Allowed origins must reflect the exact URLs where the OnTrack app will be accessed. + # Failure to update this variable with the correct origins will cause inaccessibility. + DF_ALLOWED_ORIGINS: "http://localhost:4200,https://example.com" + DF_STUDENT_WORK_DIR: /student-work DF_INSTITUTION_HOST: http://localhost:3000 DF_INSTITUTION_PRODUCT_NAME: OnTrack From 6b731f82a3ad87f4badbdf1bed05e5abcdd07d2c Mon Sep 17 00:00:00 2001 From: Epitacio Neto Date: Tue, 10 Dec 2024 14:33:33 +1100 Subject: [PATCH 2/4] CORS security patch fix on files docker-compose.yml and config/application.rb --- config/application.rb.old | 245 ++++++++++++++++++++++++++++++++++++++ docker-compose.yml.old | 73 ++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 config/application.rb.old create mode 100644 docker-compose.yml.old diff --git a/config/application.rb.old b/config/application.rb.old new file mode 100644 index 000000000..9477e5e8c --- /dev/null +++ b/config/application.rb.old @@ -0,0 +1,245 @@ +require File.expand_path('../boot', __FILE__) +require 'rails/all' +require 'csv' +require 'yaml' +require 'bunny-pub-sub/services_manager' + +# Precompile assets before deploying to production +if defined?(Bundler) + Bundler.require(*Rails.groups(assets: %w(development test))) +end + +module Doubtfire + # + # Doubtfire generic application configuration + # + class Application < Rails::Application + config.load_defaults 7.0 + + # Load .env variables + Dotenv::Rails.load + + # ==> Authentication Method + # Authentication method default is database, but possible settings + # are: database, ldap, aaf, or saml. It can be overridden using the DF_AUTH_METHOD + # environment variable. + config.auth_method = (ENV['DF_AUTH_METHOD'] || :database).to_sym + + # ==> Student work directory + # File server location for storing student's work. Defaults to `student_work` + # directory under root but is overridden using DF_STUDENT_WORK_DIR environment + # variable. + config.student_work_dir = ENV['DF_STUDENT_WORK_DIR'] || "#{Rails.root}/student_work" + + # ==> Load credentials from env + credentials.secret_key_base = ENV.fetch('DF_SECRET_KEY_BASE', Rails.env.production? ? nil : '9e010ee2f52af762916406fd2ac488c5694a6cc784777136e657511f8bbc7a73f96d59c0a9a778a0d7cf6406f8ecbf77efe4701dfbd63d8248fc7cc7f32dea97') + credentials.secret_key_attr = ENV.fetch('DF_SECRET_KEY_ATTR', Rails.env.production? ? nil : 'e69fc5960ca0e8700844a3a25fe80373b41c0a265d342eba06950113f3766fd983bad9ec51bf36eb615d9711bfe1dd90b8e35f01841b323f604ffee857e32055') + credentials.secret_key_devise = ENV.fetch('DF_SECRET_KEY_DEVISE', Rails.env.production? ? nil : 'f4e23c4388dc600e503a09ad057b8271d8fcf4c2cd6723b44f33db638e49075fe96bc545eed9110ded0c5df505625d4e1c838b718349eecf1d39270d0829d5b9') + credentials.secret_key_aaf = ENV.fetch('DF_SECRET_KEY_AAF', Rails.env.production? ? nil : 'secretsecret12345') + credentials.secret_key_moss = ENV.fetch('DF_SECRET_KEY_MOSS', nil) + + # Limit number of pdf generators to run at once + config.pdfgen_max_processes = ENV['DF_MAX_PDF_GEN_PROCESSES'] || 2 + + # Date range for auditors to view + config.auditor_unit_access_years = ENV.fetch('DF_AUDITOR_UNIT_ACCESS_YEARS', 2).years + + # ==> Institution settings + # Institution YAML and ENV (override) config load + config.institution = YAML.load_file("#{Rails.root}/config/institution.yml").with_indifferent_access + config.institution[:name] = ENV['DF_INSTITUTION_NAME'] if ENV['DF_INSTITUTION_NAME'] + config.institution[:email_domain] = ENV['DF_INSTITUTION_EMAIL_DOMAIN'] if ENV['DF_INSTITUTION_EMAIL_DOMAIN'] + config.institution[:host] = ENV['DF_INSTITUTION_HOST'] if ENV['DF_INSTITUTION_HOST'] + config.institution[:product_name] = ENV['DF_INSTITUTION_PRODUCT_NAME'] if ENV['DF_INSTITUTION_PRODUCT_NAME'] + config.institution[:privacy] = ENV['DF_INSTITUTION_PRIVACY'] if ENV['DF_INSTITUTION_PRIVACY'] + config.institution[:plagiarism] = ENV['DF_INSTITUTION_PLAGIARISM'] if ENV['DF_INSTITUTION_PLAGIARISM'] + # Institution host becomes localhost in development + config.institution[:host] ||= 'http://localhost:3000' if Rails.env.development? + config.institution[:settings] = ENV['DF_INSTITUTION_SETTINGS_RB'] if ENV['DF_INSTITUTION_SETTINGS_RB'] + config.institution[:ffmpeg] = ENV['DF_FFMPEG_PATH'] || 'ffmpeg' + + require "#{Rails.root}/config/#{config.institution[:settings]}" unless config.institution[:settings].nil? + + # ==> SAML2.0 authentication + if config.auth_method == :saml + config.saml = HashWithIndifferentAccess.new + # URL of the XML SAML Metadata (if available). + config.saml[:SAML_metadata_url] = ENV.fetch('DF_SAML_METADATA_URL', nil) + # URL to return the SAML response to (e.g., 'https://doubtfire.edu/api/auth/jwt' + config.saml[:assertion_consumer_service_url] = ENV.fetch('DF_SAML_CONSUMER_SERVICE_URL', nil) + # URL of the registered application (e.g., https://doubtfire.unifoo.edu.au) + config.saml[:entity_id] = ENV.fetch('DF_SAML_SP_ENTITY_ID', nil) + # The IDP SAML login URL, (e.g., "https://login.microsoftonline.com/xxxx/saml2") + config.saml[:idp_sso_target_url] = ENV.fetch('DF_SAML_IDP_TARGET_URL', nil) + # The IDP SAML logout URL, (e.g., "https://login.microsoftonline.com/xxxx/saml2") + config.saml[:idp_sso_signout_url] = ENV.fetch('DF_SAML_IDP_SIGNOUT_URL', nil) + + # The SAML response certificate and name format (if no XML URL metadata is provided) + if config.saml[:SAML_metadata_url].nil? + config.saml[:idp_sso_cert] = ENV.fetch('DF_SAML_IDP_CERT', nil) + + # One of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent, urn:oasis:names:tc:SAML:2.0:nameid-format:transient, urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress + # urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified, urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName, urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName + # urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos, urn:oasis:names:tc:SAML:2.0:nameid-format:entity + config.saml[:idp_name_identifier_format] = ENV['DF_SAML_IDP_SAML_NAME_IDENTIFIER_FORMAT'] || "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" + end + + # Check we have all values + # always need: + if config.saml[:assertion_consumer_service_url].nil? || + config.saml[:entity_id].nil? || + config.saml[:idp_sso_target_url].nil? + raise "Invalid values specified to saml, check the following environment variables: \n " \ + "key => variable set?\n " \ + "DF_SAML_CONSUMER_SERVICE_URL => #{!ENV['DF_SAML_CONSUMER_SERVICE_URL'].nil?}\n " \ + "DF_SAML_SP_ENTITY_ID => #{!ENV['DF_SAML_SP_ENTITY_ID'].nil?}\n " \ + "DF_SAML_IDP_SIGNOUT_URL => #{!ENV['DF_SAML_IDP_SIGNOUT_URL'].nil?}\n " \ + "DF_SAML_IDP_TARGET_URL => #{!ENV['DF_SAML_IDP_TARGET_URL'].nil?}\n" + end + + # If there's no XML url, we need the cert + if config.saml[:SAML_metadata_url].nil? && + config.saml[:idp_sso_cert].nil? + raise "Missing IDP certificate for SAML config: \n" + end + end + + # ==> AAF authentication + # Must require AAF devise authentication method. + if config.auth_method == :aaf + config.aaf = HashWithIndifferentAccess.new + # URL of the issuer (i.e., https://rapid.[test.]aaf.edu.au) + config.aaf[:issuer_url] = ENV['DF_AAF_ISSUER_URL'] || 'https://rapid.test.aaf.edu.au' + # URL of the registered application (e.g., https://doubtfire.unifoo.edu.au) + config.aaf[:audience_url] = ENV.fetch('DF_AAF_AUDIENCE_URL', nil) + # The secure URL within your application that AAF Rapid Connect should + # POST responses to (e.g., https://doubtfire.unifoo.edu.au/auth/jwt) + config.aaf[:callback_url] = ENV.fetch('DF_AAF_CALLBACK_URL', nil) + # URL of the unique url provided by rapid connect used for redirect + # (e.g., https://rapid.aaf.edu.au/jwt/authnrequest/auresearch/XXXXXXX) + config.aaf[:redirect_url] = ENV.fetch('DF_AAF_UNIQUE_URL', nil) + # URL of the identity provider (e.g., https://unifoo.edu.au/idp/shibboleth) + config.aaf[:identity_provider_url] = ENV.fetch('DF_AAF_IDENTITY_PROVIDER_URL', nil) + # The URL to redirect to after a signout + config.aaf[:auth_signout_url] = ENV.fetch('DF_AAF_AUTH_SIGNOUT_URL', nil) + # Redirection URL to use on front-end + config.aaf[:redirect_url] += "?entityID=#{config.aaf[:identity_provider_url]}" + # Check we have all values + if config.aaf[:audience_url].nil? || + config.aaf[:callback_url].nil? || + config.aaf[:redirect_url].nil? || + config.aaf[:identity_provider_url].nil? + raise "Invalid values specified to AAF, check the following environment variables: \n " \ + "key => variable set?\n " \ + "DF_AAF_ISSUER_URL => #{!ENV['DF_AAF_ISSUER_URL'].nil?}\n " \ + "DF_AAF_AUDIENCE_URL => #{!ENV['DF_AAF_AUDIENCE_URL'].nil?}\n " \ + "DF_AAF_CALLBACK_URL => #{!ENV['DF_AAF_CALLBACK_URL'].nil?}\n " \ + "DF_AAF_IDENTITY_PROVIDER_URL => #{!ENV['DF_AAF_IDENTITY_PROVIDER_URL'].nil?}\n " \ + "DF_AAF_UNIQUE_URL => #{!ENV['DF_AAF_UNIQUE_URL'].nil?}\n " \ + "DF_SECRET_KEY_AAF => #{!secrets.secret_key_aaf.nil?}\n" + end + end + # Check secrets set for DF_SECRET_KEY_BASE, DF_SECRET_KEY_ATTR, DF_SECRET_KEY_DEVISE + if credentials.secret_key_base.nil? || + credentials.secret_key_attr.nil? || + credentials.secret_key_devise.nil? + raise "Required keys are not set, check the following environment variables: \n " \ + "key => variable set?\n " \ + "DF_SECRET_KEY_BASE => #{!credentials.secret_key_base.nil?}\n " \ + "DF_SECRET_KEY_ATTR => #{!credentials.secret_key_base.nil?}\n " \ + "DF_SECRET_KEY_DEVISE => #{!credentials.secret_key_base.nil?}" + end + + # Localization + config.i18n.enforce_available_locales = true + # Ensure that auth tokens do not appear in log files + config.filter_parameters += %i( + auth_token + password + password_confirmation + ) + # Grape Serialization + + # config.paths.add 'app/api', glob: '**/*.rb' + # config.autoload_paths += Dir["#{Rails.root}/app"] + # config.autoload_paths += Dir[Rails.root.join("app", "models", "{*/}")] + + config.autoload_paths << + Rails.root.join('app') << + Rails.root.join('app', 'models', 'comments') << + Rails.root.join('app', 'models', 'turn_it_in') << + Rails.root.join('app', 'models', 'similarity') + + config.eager_load_paths << + Rails.root.join('app') << + Rails.root.join('app', 'models', 'comments') << + Rails.root.join('app', 'models', 'turn_it_in') << + Rails.root.join('app', 'models', 'similarity') + + # CORS config + config.middleware.insert_before Warden::Manager, Rack::Cors do + allow do + origins '*' + resource '*', headers: :any, methods: %i(get post put delete options) + end + end + # Generators for test framework + if Rails.env.test? + config.generators do |g| + g.test_framework :minitest, + fixtures: true, + view_specs: false, + helper_specs: false, + routing_specs: false, + controller_specs: true, + request_specs: true + end + end + + config.sm_instance = nil + config.overseer_enabled = ENV['OVERSEER_ENABLED'].present? && ENV['OVERSEER_ENABLED'].to_s.downcase != "false" && ENV['OVERSEER_ENABLED'].to_i != 0 + + if (config.overseer_enabled) + config.docker_config = { + DOCKER_REGISTRY_URL: ENV.fetch('DOCKER_REGISTRY_URL', nil), + DOCKER_PROXY_URL: ENV.fetch('DOCKER_PROXY_URL', nil), + DOCKER_TOKEN: ENV.fetch('DOCKER_TOKEN', nil), + DOCKER_USER: ENV.fetch('DOCKER_USER', nil) + } + + publisher_config = { + RABBITMQ_HOSTNAME: ENV.fetch('RABBITMQ_HOSTNAME', nil), + RABBITMQ_USERNAME: ENV.fetch('RABBITMQ_USERNAME', nil), + RABBITMQ_PASSWORD: ENV.fetch('RABBITMQ_PASSWORD', nil), + EXCHANGE_NAME: 'ontrack', + DURABLE_QUEUE_NAME: 'q.tasks', + # Publisher specific key -- all publishers will post task submissions with this key + ROUTING_KEY: 'task.submission' + } + + subscriber_config = { + RABBITMQ_HOSTNAME: ENV.fetch('RABBITMQ_HOSTNAME', nil), + RABBITMQ_USERNAME: ENV.fetch('RABBITMQ_USERNAME', nil), + RABBITMQ_PASSWORD: ENV.fetch('RABBITMQ_PASSWORD', nil), + EXCHANGE_NAME: 'ontrack', + DURABLE_QUEUE_NAME: 'q.overseer', + # No need to define BINDING_KEYS for now! + # In future, OnTrack will listen to + # topics related to PDF generation too. + # That is when we should have BINDING_KEYS defined. + # BINDING_KEYS: ENV['BINDING_KEYS'], + + # This is enough for now: + DEFAULT_BINDING_KEY: '*.result' + } + + if config.docker_config[:DOCKER_TOKEN] && config.docker_config[:DOCKER_PROXY_URL] + # TODO: move to sidekiq + `echo \"${DOCKER_TOKEN}\" | docker login --username ${DOCKER_USER} --password-stdin ${DOCKER_PROXY_URL} >> /dev/null 2>&1` + end + + config.sm_instance = ServicesManager.instance + config.sm_instance.register_client(:ontrack, publisher_config, subscriber_config) + end + end +end diff --git a/docker-compose.yml.old b/docker-compose.yml.old new file mode 100644 index 000000000..22520ccaa --- /dev/null +++ b/docker-compose.yml.old @@ -0,0 +1,73 @@ +version: '3' +services: + df-api: + container_name: df-api + build: . + ports: + - "3000:3000" + volumes: + - ./:/doubtfire + - ../data/tmp:/doubtfire/tmp + - ../data/student-work:/student-work + depends_on: + - dev-db + environment: + RAILS_ENV: 'development' + + DF_STUDENT_WORK_DIR: /student-work + DF_INSTITUTION_HOST: http://localhost:3000 + DF_INSTITUTION_PRODUCT_NAME: OnTrack + DF_INSTITUTION_PRODUCT_VERSION: 0.0.1 + DF_INSTITUTION_NAME: Doubtfire + + DF_SECRET_KEY_BASE: test-secret-key-test-secret-key! + DF_SECRET_KEY_ATTR: test-secret-key-test-secret-key! + DF_SECRET_KEY_DEVISE: test-secret-key-test-secret-key! + + # Authentication method - can set to AAF or ldap + DF_AUTH_METHOD: database + DF_AAF_ISSUER_URL: https://rapid.test.aaf.edu.au + DF_AAF_AUDIENCE_URL: http://localhost:3000 + DF_AAF_CALLBACK_URL: http://localhost:3000/api/auth/jwt + DF_AAF_IDENTITY_PROVIDER_URL: https://signon-uat.deakin.edu.au/idp/shibboleth + DF_AAF_UNIQUE_URL: https://rapid.test.aaf.edu.au/jwt/authnrequest/research/Ag4EJJhjf0zXHqlKvKZEbg + DF_AAF_AUTH_SIGNOUT_URL: https://sync-uat.deakin.edu.au/auth/logout + DF_SECRET_KEY_AAF: v4~LMFLzzwRGZdju\5QBa@FiHIN9 + + # Database settings - for development env + DF_DEV_DB_ADAPTER: mysql2 + DF_DEV_DB_HOST: doubtfire-dev-db + DF_DEV_DB_DATABASE: doubtfire-dev + DF_DEV_DB_USERNAME: dfire + DF_DEV_DB_PASSWORD: pwd + + # Database settings - for test env + DF_TEST_DB_ADAPTER: mysql2 + DF_TEST_DB_HOST: doubtfire-dev-db + DF_TEST_DB_DATABASE: doubtfire-dev + DF_TEST_DB_USERNAME: dfire + DF_TEST_DB_PASSWORD: pwd + + # Database settings - for test env + DF_PRODUCTION_DB_ADAPTER: mysql2 + DF_PRODUCTION_DB_HOST: doubtfire-dev-db + DF_PRODUCTION_DB_DATABASE: doubtfire-dev + DF_PRODUCTION_DB_USERNAME: dfire + DF_PRODUCTION_DB_PASSWORD: pwd + + # Overseer + OVERSEER_ENABLED: 0 + # RABBITMQ_HOSTNAME: doubtfire-mq + # RABBITMQ_USERNAME: secure_credentials + # RABBITMQ_PASSWORD: secure_credentials + + dev-db: + container_name: doubtfire-dev-db + image: mariadb + environment: + MYSQL_ROOT_PASSWORD: db-root-password + MYSQL_DATABASE: doubtfire-dev + MYSQL_USER: dfire + MYSQL_PASSWORD: pwd + volumes: + - ../data/database:/var/lib/mysql From 3002309a6c13f094a5c761d345132c12db44bce4 Mon Sep 17 00:00:00 2001 From: Epitacio Neto Date: Tue, 10 Dec 2024 17:43:56 +1100 Subject: [PATCH 3/4] Remove old backup files application.rb.old and docker-compose.yml.old --- config/application.rb.old | 245 -------------------------------------- docker-compose.yml.old | 73 ------------ 2 files changed, 318 deletions(-) delete mode 100644 config/application.rb.old delete mode 100644 docker-compose.yml.old diff --git a/config/application.rb.old b/config/application.rb.old deleted file mode 100644 index 9477e5e8c..000000000 --- a/config/application.rb.old +++ /dev/null @@ -1,245 +0,0 @@ -require File.expand_path('../boot', __FILE__) -require 'rails/all' -require 'csv' -require 'yaml' -require 'bunny-pub-sub/services_manager' - -# Precompile assets before deploying to production -if defined?(Bundler) - Bundler.require(*Rails.groups(assets: %w(development test))) -end - -module Doubtfire - # - # Doubtfire generic application configuration - # - class Application < Rails::Application - config.load_defaults 7.0 - - # Load .env variables - Dotenv::Rails.load - - # ==> Authentication Method - # Authentication method default is database, but possible settings - # are: database, ldap, aaf, or saml. It can be overridden using the DF_AUTH_METHOD - # environment variable. - config.auth_method = (ENV['DF_AUTH_METHOD'] || :database).to_sym - - # ==> Student work directory - # File server location for storing student's work. Defaults to `student_work` - # directory under root but is overridden using DF_STUDENT_WORK_DIR environment - # variable. - config.student_work_dir = ENV['DF_STUDENT_WORK_DIR'] || "#{Rails.root}/student_work" - - # ==> Load credentials from env - credentials.secret_key_base = ENV.fetch('DF_SECRET_KEY_BASE', Rails.env.production? ? nil : '9e010ee2f52af762916406fd2ac488c5694a6cc784777136e657511f8bbc7a73f96d59c0a9a778a0d7cf6406f8ecbf77efe4701dfbd63d8248fc7cc7f32dea97') - credentials.secret_key_attr = ENV.fetch('DF_SECRET_KEY_ATTR', Rails.env.production? ? nil : 'e69fc5960ca0e8700844a3a25fe80373b41c0a265d342eba06950113f3766fd983bad9ec51bf36eb615d9711bfe1dd90b8e35f01841b323f604ffee857e32055') - credentials.secret_key_devise = ENV.fetch('DF_SECRET_KEY_DEVISE', Rails.env.production? ? nil : 'f4e23c4388dc600e503a09ad057b8271d8fcf4c2cd6723b44f33db638e49075fe96bc545eed9110ded0c5df505625d4e1c838b718349eecf1d39270d0829d5b9') - credentials.secret_key_aaf = ENV.fetch('DF_SECRET_KEY_AAF', Rails.env.production? ? nil : 'secretsecret12345') - credentials.secret_key_moss = ENV.fetch('DF_SECRET_KEY_MOSS', nil) - - # Limit number of pdf generators to run at once - config.pdfgen_max_processes = ENV['DF_MAX_PDF_GEN_PROCESSES'] || 2 - - # Date range for auditors to view - config.auditor_unit_access_years = ENV.fetch('DF_AUDITOR_UNIT_ACCESS_YEARS', 2).years - - # ==> Institution settings - # Institution YAML and ENV (override) config load - config.institution = YAML.load_file("#{Rails.root}/config/institution.yml").with_indifferent_access - config.institution[:name] = ENV['DF_INSTITUTION_NAME'] if ENV['DF_INSTITUTION_NAME'] - config.institution[:email_domain] = ENV['DF_INSTITUTION_EMAIL_DOMAIN'] if ENV['DF_INSTITUTION_EMAIL_DOMAIN'] - config.institution[:host] = ENV['DF_INSTITUTION_HOST'] if ENV['DF_INSTITUTION_HOST'] - config.institution[:product_name] = ENV['DF_INSTITUTION_PRODUCT_NAME'] if ENV['DF_INSTITUTION_PRODUCT_NAME'] - config.institution[:privacy] = ENV['DF_INSTITUTION_PRIVACY'] if ENV['DF_INSTITUTION_PRIVACY'] - config.institution[:plagiarism] = ENV['DF_INSTITUTION_PLAGIARISM'] if ENV['DF_INSTITUTION_PLAGIARISM'] - # Institution host becomes localhost in development - config.institution[:host] ||= 'http://localhost:3000' if Rails.env.development? - config.institution[:settings] = ENV['DF_INSTITUTION_SETTINGS_RB'] if ENV['DF_INSTITUTION_SETTINGS_RB'] - config.institution[:ffmpeg] = ENV['DF_FFMPEG_PATH'] || 'ffmpeg' - - require "#{Rails.root}/config/#{config.institution[:settings]}" unless config.institution[:settings].nil? - - # ==> SAML2.0 authentication - if config.auth_method == :saml - config.saml = HashWithIndifferentAccess.new - # URL of the XML SAML Metadata (if available). - config.saml[:SAML_metadata_url] = ENV.fetch('DF_SAML_METADATA_URL', nil) - # URL to return the SAML response to (e.g., 'https://doubtfire.edu/api/auth/jwt' - config.saml[:assertion_consumer_service_url] = ENV.fetch('DF_SAML_CONSUMER_SERVICE_URL', nil) - # URL of the registered application (e.g., https://doubtfire.unifoo.edu.au) - config.saml[:entity_id] = ENV.fetch('DF_SAML_SP_ENTITY_ID', nil) - # The IDP SAML login URL, (e.g., "https://login.microsoftonline.com/xxxx/saml2") - config.saml[:idp_sso_target_url] = ENV.fetch('DF_SAML_IDP_TARGET_URL', nil) - # The IDP SAML logout URL, (e.g., "https://login.microsoftonline.com/xxxx/saml2") - config.saml[:idp_sso_signout_url] = ENV.fetch('DF_SAML_IDP_SIGNOUT_URL', nil) - - # The SAML response certificate and name format (if no XML URL metadata is provided) - if config.saml[:SAML_metadata_url].nil? - config.saml[:idp_sso_cert] = ENV.fetch('DF_SAML_IDP_CERT', nil) - - # One of urn:oasis:names:tc:SAML:2.0:nameid-format:persistent, urn:oasis:names:tc:SAML:2.0:nameid-format:transient, urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress - # urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified, urn:oasis:names:tc:SAML:1.1:nameid-format:X509SubjectName, urn:oasis:names:tc:SAML:1.1:nameid-format:WindowsDomainQualifiedName - # urn:oasis:names:tc:SAML:2.0:nameid-format:kerberos, urn:oasis:names:tc:SAML:2.0:nameid-format:entity - config.saml[:idp_name_identifier_format] = ENV['DF_SAML_IDP_SAML_NAME_IDENTIFIER_FORMAT'] || "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress" - end - - # Check we have all values - # always need: - if config.saml[:assertion_consumer_service_url].nil? || - config.saml[:entity_id].nil? || - config.saml[:idp_sso_target_url].nil? - raise "Invalid values specified to saml, check the following environment variables: \n " \ - "key => variable set?\n " \ - "DF_SAML_CONSUMER_SERVICE_URL => #{!ENV['DF_SAML_CONSUMER_SERVICE_URL'].nil?}\n " \ - "DF_SAML_SP_ENTITY_ID => #{!ENV['DF_SAML_SP_ENTITY_ID'].nil?}\n " \ - "DF_SAML_IDP_SIGNOUT_URL => #{!ENV['DF_SAML_IDP_SIGNOUT_URL'].nil?}\n " \ - "DF_SAML_IDP_TARGET_URL => #{!ENV['DF_SAML_IDP_TARGET_URL'].nil?}\n" - end - - # If there's no XML url, we need the cert - if config.saml[:SAML_metadata_url].nil? && - config.saml[:idp_sso_cert].nil? - raise "Missing IDP certificate for SAML config: \n" - end - end - - # ==> AAF authentication - # Must require AAF devise authentication method. - if config.auth_method == :aaf - config.aaf = HashWithIndifferentAccess.new - # URL of the issuer (i.e., https://rapid.[test.]aaf.edu.au) - config.aaf[:issuer_url] = ENV['DF_AAF_ISSUER_URL'] || 'https://rapid.test.aaf.edu.au' - # URL of the registered application (e.g., https://doubtfire.unifoo.edu.au) - config.aaf[:audience_url] = ENV.fetch('DF_AAF_AUDIENCE_URL', nil) - # The secure URL within your application that AAF Rapid Connect should - # POST responses to (e.g., https://doubtfire.unifoo.edu.au/auth/jwt) - config.aaf[:callback_url] = ENV.fetch('DF_AAF_CALLBACK_URL', nil) - # URL of the unique url provided by rapid connect used for redirect - # (e.g., https://rapid.aaf.edu.au/jwt/authnrequest/auresearch/XXXXXXX) - config.aaf[:redirect_url] = ENV.fetch('DF_AAF_UNIQUE_URL', nil) - # URL of the identity provider (e.g., https://unifoo.edu.au/idp/shibboleth) - config.aaf[:identity_provider_url] = ENV.fetch('DF_AAF_IDENTITY_PROVIDER_URL', nil) - # The URL to redirect to after a signout - config.aaf[:auth_signout_url] = ENV.fetch('DF_AAF_AUTH_SIGNOUT_URL', nil) - # Redirection URL to use on front-end - config.aaf[:redirect_url] += "?entityID=#{config.aaf[:identity_provider_url]}" - # Check we have all values - if config.aaf[:audience_url].nil? || - config.aaf[:callback_url].nil? || - config.aaf[:redirect_url].nil? || - config.aaf[:identity_provider_url].nil? - raise "Invalid values specified to AAF, check the following environment variables: \n " \ - "key => variable set?\n " \ - "DF_AAF_ISSUER_URL => #{!ENV['DF_AAF_ISSUER_URL'].nil?}\n " \ - "DF_AAF_AUDIENCE_URL => #{!ENV['DF_AAF_AUDIENCE_URL'].nil?}\n " \ - "DF_AAF_CALLBACK_URL => #{!ENV['DF_AAF_CALLBACK_URL'].nil?}\n " \ - "DF_AAF_IDENTITY_PROVIDER_URL => #{!ENV['DF_AAF_IDENTITY_PROVIDER_URL'].nil?}\n " \ - "DF_AAF_UNIQUE_URL => #{!ENV['DF_AAF_UNIQUE_URL'].nil?}\n " \ - "DF_SECRET_KEY_AAF => #{!secrets.secret_key_aaf.nil?}\n" - end - end - # Check secrets set for DF_SECRET_KEY_BASE, DF_SECRET_KEY_ATTR, DF_SECRET_KEY_DEVISE - if credentials.secret_key_base.nil? || - credentials.secret_key_attr.nil? || - credentials.secret_key_devise.nil? - raise "Required keys are not set, check the following environment variables: \n " \ - "key => variable set?\n " \ - "DF_SECRET_KEY_BASE => #{!credentials.secret_key_base.nil?}\n " \ - "DF_SECRET_KEY_ATTR => #{!credentials.secret_key_base.nil?}\n " \ - "DF_SECRET_KEY_DEVISE => #{!credentials.secret_key_base.nil?}" - end - - # Localization - config.i18n.enforce_available_locales = true - # Ensure that auth tokens do not appear in log files - config.filter_parameters += %i( - auth_token - password - password_confirmation - ) - # Grape Serialization - - # config.paths.add 'app/api', glob: '**/*.rb' - # config.autoload_paths += Dir["#{Rails.root}/app"] - # config.autoload_paths += Dir[Rails.root.join("app", "models", "{*/}")] - - config.autoload_paths << - Rails.root.join('app') << - Rails.root.join('app', 'models', 'comments') << - Rails.root.join('app', 'models', 'turn_it_in') << - Rails.root.join('app', 'models', 'similarity') - - config.eager_load_paths << - Rails.root.join('app') << - Rails.root.join('app', 'models', 'comments') << - Rails.root.join('app', 'models', 'turn_it_in') << - Rails.root.join('app', 'models', 'similarity') - - # CORS config - config.middleware.insert_before Warden::Manager, Rack::Cors do - allow do - origins '*' - resource '*', headers: :any, methods: %i(get post put delete options) - end - end - # Generators for test framework - if Rails.env.test? - config.generators do |g| - g.test_framework :minitest, - fixtures: true, - view_specs: false, - helper_specs: false, - routing_specs: false, - controller_specs: true, - request_specs: true - end - end - - config.sm_instance = nil - config.overseer_enabled = ENV['OVERSEER_ENABLED'].present? && ENV['OVERSEER_ENABLED'].to_s.downcase != "false" && ENV['OVERSEER_ENABLED'].to_i != 0 - - if (config.overseer_enabled) - config.docker_config = { - DOCKER_REGISTRY_URL: ENV.fetch('DOCKER_REGISTRY_URL', nil), - DOCKER_PROXY_URL: ENV.fetch('DOCKER_PROXY_URL', nil), - DOCKER_TOKEN: ENV.fetch('DOCKER_TOKEN', nil), - DOCKER_USER: ENV.fetch('DOCKER_USER', nil) - } - - publisher_config = { - RABBITMQ_HOSTNAME: ENV.fetch('RABBITMQ_HOSTNAME', nil), - RABBITMQ_USERNAME: ENV.fetch('RABBITMQ_USERNAME', nil), - RABBITMQ_PASSWORD: ENV.fetch('RABBITMQ_PASSWORD', nil), - EXCHANGE_NAME: 'ontrack', - DURABLE_QUEUE_NAME: 'q.tasks', - # Publisher specific key -- all publishers will post task submissions with this key - ROUTING_KEY: 'task.submission' - } - - subscriber_config = { - RABBITMQ_HOSTNAME: ENV.fetch('RABBITMQ_HOSTNAME', nil), - RABBITMQ_USERNAME: ENV.fetch('RABBITMQ_USERNAME', nil), - RABBITMQ_PASSWORD: ENV.fetch('RABBITMQ_PASSWORD', nil), - EXCHANGE_NAME: 'ontrack', - DURABLE_QUEUE_NAME: 'q.overseer', - # No need to define BINDING_KEYS for now! - # In future, OnTrack will listen to - # topics related to PDF generation too. - # That is when we should have BINDING_KEYS defined. - # BINDING_KEYS: ENV['BINDING_KEYS'], - - # This is enough for now: - DEFAULT_BINDING_KEY: '*.result' - } - - if config.docker_config[:DOCKER_TOKEN] && config.docker_config[:DOCKER_PROXY_URL] - # TODO: move to sidekiq - `echo \"${DOCKER_TOKEN}\" | docker login --username ${DOCKER_USER} --password-stdin ${DOCKER_PROXY_URL} >> /dev/null 2>&1` - end - - config.sm_instance = ServicesManager.instance - config.sm_instance.register_client(:ontrack, publisher_config, subscriber_config) - end - end -end diff --git a/docker-compose.yml.old b/docker-compose.yml.old deleted file mode 100644 index 22520ccaa..000000000 --- a/docker-compose.yml.old +++ /dev/null @@ -1,73 +0,0 @@ -version: '3' -services: - df-api: - container_name: df-api - build: . - ports: - - "3000:3000" - volumes: - - ./:/doubtfire - - ../data/tmp:/doubtfire/tmp - - ../data/student-work:/student-work - depends_on: - - dev-db - environment: - RAILS_ENV: 'development' - - DF_STUDENT_WORK_DIR: /student-work - DF_INSTITUTION_HOST: http://localhost:3000 - DF_INSTITUTION_PRODUCT_NAME: OnTrack - DF_INSTITUTION_PRODUCT_VERSION: 0.0.1 - DF_INSTITUTION_NAME: Doubtfire - - DF_SECRET_KEY_BASE: test-secret-key-test-secret-key! - DF_SECRET_KEY_ATTR: test-secret-key-test-secret-key! - DF_SECRET_KEY_DEVISE: test-secret-key-test-secret-key! - - # Authentication method - can set to AAF or ldap - DF_AUTH_METHOD: database - DF_AAF_ISSUER_URL: https://rapid.test.aaf.edu.au - DF_AAF_AUDIENCE_URL: http://localhost:3000 - DF_AAF_CALLBACK_URL: http://localhost:3000/api/auth/jwt - DF_AAF_IDENTITY_PROVIDER_URL: https://signon-uat.deakin.edu.au/idp/shibboleth - DF_AAF_UNIQUE_URL: https://rapid.test.aaf.edu.au/jwt/authnrequest/research/Ag4EJJhjf0zXHqlKvKZEbg - DF_AAF_AUTH_SIGNOUT_URL: https://sync-uat.deakin.edu.au/auth/logout - DF_SECRET_KEY_AAF: v4~LMFLzzwRGZdju\5QBa@FiHIN9 - - # Database settings - for development env - DF_DEV_DB_ADAPTER: mysql2 - DF_DEV_DB_HOST: doubtfire-dev-db - DF_DEV_DB_DATABASE: doubtfire-dev - DF_DEV_DB_USERNAME: dfire - DF_DEV_DB_PASSWORD: pwd - - # Database settings - for test env - DF_TEST_DB_ADAPTER: mysql2 - DF_TEST_DB_HOST: doubtfire-dev-db - DF_TEST_DB_DATABASE: doubtfire-dev - DF_TEST_DB_USERNAME: dfire - DF_TEST_DB_PASSWORD: pwd - - # Database settings - for test env - DF_PRODUCTION_DB_ADAPTER: mysql2 - DF_PRODUCTION_DB_HOST: doubtfire-dev-db - DF_PRODUCTION_DB_DATABASE: doubtfire-dev - DF_PRODUCTION_DB_USERNAME: dfire - DF_PRODUCTION_DB_PASSWORD: pwd - - # Overseer - OVERSEER_ENABLED: 0 - # RABBITMQ_HOSTNAME: doubtfire-mq - # RABBITMQ_USERNAME: secure_credentials - # RABBITMQ_PASSWORD: secure_credentials - - dev-db: - container_name: doubtfire-dev-db - image: mariadb - environment: - MYSQL_ROOT_PASSWORD: db-root-password - MYSQL_DATABASE: doubtfire-dev - MYSQL_USER: dfire - MYSQL_PASSWORD: pwd - volumes: - - ../data/database:/var/lib/mysql From 9fb6ce91808117494e1a91e083c282e299141a05 Mon Sep 17 00:00:00 2001 From: epineto <96008456+epineto@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:59:14 +1100 Subject: [PATCH 4/4] Update docker-compose.yml (Removing example.com) --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index e00ccd416..9a97ee300 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -18,7 +18,7 @@ services: # The DF_ALLOWED_ORIGINS variable must reflect the exact URLs where the OnTrack app will be accessed (e.g., production, staging, or development URLs). # Allowed origins must reflect the exact URLs where the OnTrack app will be accessed. # Failure to update this variable with the correct origins will cause inaccessibility. - DF_ALLOWED_ORIGINS: "http://localhost:4200,https://example.com" + DF_ALLOWED_ORIGINS: "http://localhost:4200" DF_STUDENT_WORK_DIR: /student-work DF_INSTITUTION_HOST: http://localhost:3000