diff --git a/.circleci/config.yml b/.circleci/config.yml deleted file mode 100644 index 21661d4..0000000 --- a/.circleci/config.yml +++ /dev/null @@ -1,47 +0,0 @@ ---- - -version: 2 - -jobs: - build: - docker: - - image: decidim/decidim@sha256:348fb1faa49cfe76ba7cbeafc67ca09002cf95f0ffa7a76eefa9d5b3774e0ee9 - environment: - DATABASE_USERNAME: postgres - - - image: postgres - environment: - POSTGRES_USER: postgres - - working_directory: /app - - steps: - - checkout - - - restore_cache: - keys: - - bundler-dependencies-{{ checksum "Gemfile.lock" }} - - - run: - name: Install dependencies - command: bundle install - - - save_cache: - key: bundler-dependencies-{{ checksum "Gemfile.lock" }} - paths: - - /usr/local/bundle - - - run: - name: Wait for db - command: dockerize -wait tcp://localhost:5432 -timeout 1m - - - run: - name: Generate test app - command: bundle exec rake decidim:generate_external_test_app - - - run: - name: Run RSpec tests - command: bundle exec rspec - - - store_artifacts: - path: /app/spec/decidim_dummy_app/tmp/screenshots diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 938a2ed..304131a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -65,6 +65,9 @@ jobs: run: | bundle exec rake test_app + - name: set crm authenticable configuration values + run: bundle exec rake civi_crm:configure_secrets + - name: Run RSpec uses: nick-invision/retry@v2 with: diff --git a/.rubocop.yml b/.rubocop.yml index 52a1c3e..5102b5a 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -2,6 +2,7 @@ require: rubocop-rspec # Common configuration. AllCops: + NewCops: enable Include: - .simplecov - "**/*.rb" @@ -64,7 +65,7 @@ AllCops: # If a value is specified for TargetRubyVersion then it is used. # Else if .ruby-version exists and it contains an MRI version it is used. # Otherwise we fallback to the oldest officially supported Ruby version (2.0). - TargetRubyVersion: 2.6 + TargetRubyVersion: 2.7 RSpec: Patterns: @@ -88,7 +89,7 @@ Style/Alias: - prefer_alias_method # Align the elements of a hash literal if they span more than one line. -Layout/AlignHash: +Layout/HashAlignment: # Alignment of entries using hash rocket as separator. Valid values are: # # key - left alignment of keys @@ -151,7 +152,7 @@ Layout/AlignHash: - ignore_implicit - ignore_explicit -Layout/AlignParameters: +Layout/ParameterAlignment: # Alignment of parameters in multi-line method calls. # # The `with_first_parameter` style aligns the following lines along the same @@ -262,20 +263,6 @@ Style/BlockDelimiters: - proc - it -Style/BracesAroundHashParameters: - EnforcedStyle: no_braces - SupportedStyles: - # The `braces` style enforces braces around all method parameters that are - # hashes. - - braces - # The `no_braces` style checks that the last parameter doesn't have braces - # around it. - - no_braces - # The `context_dependent` style checks that the last parameter doesn't have - # braces around it, but requires braces if the second to last parameter is - # also a hash literal. - - context_dependent - # Indentation of `when`. Layout/CaseIndentation: EnforcedStyle: case @@ -468,7 +455,7 @@ Naming/FileName: # files with a shebang in the first line). IgnoreExecutableScripts: true -Layout/IndentFirstArgument: +Layout/FirstArgumentIndentation: EnforcedStyle: special_for_inner_method_call_in_parentheses SupportedStyles: # The first parameter should always be indented one step more than the @@ -558,7 +545,7 @@ Layout/IndentationWidth: Width: 2 # Checks the indentation of the first element in an array literal. -Layout/IndentFirstArrayElement: +Layout/FirstArrayElementIndentation: # The value `special_inside_parentheses` means that array literals with # brackets that have their opening bracket on the same line as a surrounding # opening round parenthesis, shall have their first element indented relative @@ -580,13 +567,13 @@ Layout/IndentFirstArrayElement: IndentationWidth: ~ # Checks the indentation of assignment RHS, when on a different line from LHS -Layout/IndentAssignment: +Layout/AssignmentIndentation: # By default, the indentation width from Style/IndentationWidth is used # But it can be overridden by setting this parameter IndentationWidth: ~ # Checks the indentation of the first key in a hash literal. -Layout/IndentFirstHashElement: +Layout/FirstHashElementIndentation: # The value `special_inside_parentheses` means that hash literals with braces # that have their opening brace on the same line as a surrounding opening # round parenthesis, shall have their first key indented relative to the @@ -793,12 +780,12 @@ Naming/PredicateName: - has_ - have_ # Predicate name prefixes that should be removed. - NamePrefixBlacklist: + ForbiddenPrefixes: - is_ - have_ # Predicate names which, despite having a blacklisted prefix, or no ?, # should still be accepted - NameWhitelist: + AllowedMethods: - is_a? # Exclude Rspec specs because there is a strong convetion to write spec # helpers in the form of `have_something` or `be_something`. @@ -976,7 +963,7 @@ Style/TernaryParentheses: - require_no_parentheses AllowSafeAssignment: true -Layout/TrailingBlankLines: +Layout/TrailingEmptyLines: EnforcedStyle: final_newline SupportedStyles: - final_newline @@ -1026,7 +1013,7 @@ Style/TrivialAccessors: # Commonly used in DSLs AllowDSLWriters: false IgnoreClassMethods: false - Whitelist: + AllowedMethods: - to_ary - to_a - to_c @@ -1100,18 +1087,6 @@ Metrics/CyclomaticComplexity: Exclude: - "**/*/permissions.rb" -Metrics/LineLength: - Max: 180 - # To make it possible to copy or click on URIs in the code, we allow lines - # containing a URI to be longer than Max. - AllowHeredoc: true - AllowURI: true - URISchemes: - - http - - https - Exclude: - - "**/spec/**/*" - Metrics/MethodLength: CountComments: false # count full line comments? Max: 15 @@ -1168,6 +1143,18 @@ Layout/DefEndAlignment: EnforcedStyleAlignWith: start_of_line AutoCorrect: false +Layout/LineLength: + Max: 180 + # To make it possible to copy or click on URIs in the code, we allow lines + # containing a URI to be longer than Max. + AllowHeredoc: true + AllowURI: true + URISchemes: + - http + - https + Exclude: + - "**/spec/**/*" + Lint/InheritException: # The default base class in favour of `Exception`. EnforcedStyle: runtime_error @@ -1187,10 +1174,6 @@ Lint/UnusedMethodArgument: ##################### Performance ############################ -Performance/RedundantMerge: - # Max number of key-value pairs to consider an offense - MaxKeyValuePairs: 2 - Metrics/BlockLength: Enabled: false @@ -1236,6 +1219,14 @@ RSpec/MessageSpies: RSpec/MultipleExpectations: Max: 17 +RSpec/MultipleMemoizedHelpers: + Exclude: + - spec/commands/decidim/create_registration_spec.rb + - spec/models/decidim/user_spec.rb + - spec/services/erc/crm_authenticable/crm_authenticable_authorization_handler_spec.rb + - spec/services/erc/crm_authenticable/user_authorizer_spec.rb + - spec/system/decidim/erc/crm_authenticable/crm_authenticable_action_authorizer_spec.rb + RSpec/NestedGroups: Max: 7 diff --git a/.rubocop_rails.yml b/.rubocop_rails.yml index e4467cf..d1cf53e 100644 --- a/.rubocop_rails.yml +++ b/.rubocop_rails.yml @@ -1,88 +1,3 @@ Rails: Enabled: true -Rails/ActionFilter: - Include: - - decidim-*/app/controllers/**/*.rb - -Rails/CreateTableWithTimestamps: - Enabled: false - -Rails/EnumUniqueness: - Include: - - decidim-*/app/models/**/*.rb - -Rails/Exit: - Include: - - decidim-*/app/**/*.rb - - decidim-*/config/**/*.rb - - decidim-*/lib/**/*.rb - Exclude: - - decidim-*/lib/**/*.rake - -Rails/FindBy: - Include: - - "**/*.rb" - -Rails/FindEach: - Include: - - decidim-*/app/models/**/*.rb - -Rails/HasAndBelongsToMany: - Include: - - decidim-*/app/models/**/*.rb - -Rails/HasManyOrHasOneDependent: - Include: - - decidim-*/app/models/**/*.rb - -Rails/InverseOf: - Enabled: false - -Rails/LexicallyScopedActionFilter: - Include: - - decidim-*/app/controllers/**/*.rb - -Rails/NotNullColumn: - Enabled: false - -Rails/Output: - Include: - - decidim-*/app/**/*.rb - - decidim-*/config/**/*.rb - - decidim-*/db/**/*.rb - - decidim-*/lib/**/*.rb - Exclude: - - decidim-core/db/seeds.rb - - decidim-core/lib/decidim/core.rb - - decidim-core/lib/decidim/component_manifest.rb - - decidim-core/lib/decidim/participatory_space_manifest.rb - - decidim-system/db/seeds.rb - -Rails/OutputSafety: - Enabled: false - -Rails/ReadWriteAttribute: - Include: - - decidim-*/app/models/**/*.rb - -Rails/ReversibleMigration: - Enabled: false - -Rails/ScopeArgs: - Include: - - decidim-*/app/models/**/*.rb - -Rails/SkipsModelValidations: - Enabled: true - -Rails/UnknownEnv: - Enabled: false - Environments: - - development - - test - - production - - preprod -Rails/Validation: - Include: - - decidim-*/app/models/**/*.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index 2786564..30c2041 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,11 @@ # Changelog +### Version 1.2.0 +- Upgrade to Decidim 0.24 + ### Version 1.1.6 - Update rake tasks with csv scopes (#28) + ### Version 1.1.5 - Change how the signup against CSV is enabled (#26) - Upgrade to Ruby 2.7.2 (#26) diff --git a/Gemfile b/Gemfile index 05a6f24..15b9476 100644 --- a/Gemfile +++ b/Gemfile @@ -8,7 +8,7 @@ gemspec require_relative "lib/decidim/erc/crm_authenticable/version" -gem "decidim", Decidim::Erc::CrmAuthenticable.decidim_version +gem "decidim", "~>#{Decidim::Erc::CrmAuthenticable.decidim_version}" group :development, :test do gem "bootsnap", require: true diff --git a/README.md b/README.md index 730b2e4..d8241c1 100644 --- a/README.md +++ b/README.md @@ -74,10 +74,11 @@ Run the following in the gem development path to create the test app: ```bash $ bundle $ bundle exec rake test_app +$ bundle exec rake civi_crm:configure_secrets ``` Note that the database user has to have rights to create and drop a database in order to create the dummy test app database. -And then set the configuration values for the test app in `spec/decidim_dummy_app/config/secrets.yml`: +And the `civi_crm:configure_secrets` taks set the configuration values for the test app in `spec/decidim_dummy_app/config/secrets.yml` as: ```yaml erc_crm_authenticable: diff --git a/Rakefile b/Rakefile index 93ac973..c11dc02 100644 --- a/Rakefile +++ b/Rakefile @@ -13,3 +13,23 @@ task test_app: "decidim:generate_external_test_app" do FileUtils.cp(filename, dest_folder) end end + +namespace :civi_crm do + desc "Configure assets required by tests" + task :configure_secrets do + values = <<-EOVALUES + erc_crm_authenticable: + api_base: https://api.base/? + site_key: site_key + api_key: api_key + secret_key: secret_key + EOVALUES + values = values.gsub(/\n/, "\\\n") + + cmd = "sed -i '/default: &default/a\\#{values}' spec/decidim_dummy_app/config/secrets.yml" + puts "---------------------------" + puts cmd + puts "---------------------------" + system(cmd) + end +end diff --git a/app/controllers/decidim/devise/sessions_controller.rb b/app/controllers/decidim/devise/sessions_controller.rb index ae46610..2787550 100644 --- a/app/controllers/decidim/devise/sessions_controller.rb +++ b/app/controllers/decidim/devise/sessions_controller.rb @@ -24,10 +24,6 @@ def create end end - def after_sign_in_path_for(user) - super - end - def pending_redirect?(user) store_location_for(user, stored_location_for(user)) end diff --git a/app/views/decidim/devise/registrations/new.html.erb b/app/views/decidim/devise/registrations/new.html.erb index f3407cb..cbf411a 100644 --- a/app/views/decidim/devise/registrations/new.html.erb +++ b/app/views/decidim/devise/registrations/new.html.erb @@ -1,4 +1,5 @@ -
+<% add_decidim_page_title(t(".title")) %> +
@@ -37,4 +38,5 @@
-
+ +
diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb new file mode 100644 index 0000000..460deea --- /dev/null +++ b/config/initializers/doorkeeper.rb @@ -0,0 +1,492 @@ +# frozen_string_literal: true + +Doorkeeper.configure do + # Change the ORM that doorkeeper will use (requires ORM extensions installed). + # Check the list of supported ORMs here: https://github.com/doorkeeper-gem/doorkeeper#orms + orm :active_record + + # This block will be called to check whether the resource owner is authenticated or not. + resource_owner_authenticator do + raise "Please configure doorkeeper resource_owner_authenticator block located in #{__FILE__}" + # Put your resource owner authentication logic here. + # Example implementation: + # User.find_by(id: session[:user_id]) || redirect_to(new_user_session_url) + end + + # If you didn't skip applications controller from Doorkeeper routes in your application routes.rb + # file then you need to declare this block in order to restrict access to the web interface for + # adding oauth authorized applications. In other case it will return 403 Forbidden response + # every time somebody will try to access the admin web interface. + # + # admin_authenticator do + # # Put your admin authentication logic here. + # # Example implementation: + # + # if current_user + # head :forbidden unless current_user.admin? + # else + # redirect_to sign_in_url + # end + # end + + # You can use your own model classes if you need to extend (or even override) default + # Doorkeeper models such as `Application`, `AccessToken` and `AccessGrant. + # + # Be default Doorkeeper ActiveRecord ORM uses it's own classes: + # + # access_token_class "Doorkeeper::AccessToken" + # access_grant_class "Doorkeeper::AccessGrant" + # application_class "Doorkeeper::Application" + # + # Don't forget to include Doorkeeper ORM mixins into your custom models: + # + # * ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken - for access token + # * ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessGrant - for access grant + # * ::Doorkeeper::Orm::ActiveRecord::Mixins::Application - for application (OAuth2 clients) + # + # For example: + # + # access_token_class "MyAccessToken" + # + # class MyAccessToken < ApplicationRecord + # include ::Doorkeeper::Orm::ActiveRecord::Mixins::AccessToken + # + # self.table_name = "hey_i_wanna_my_name" + # + # def destroy_me! + # destroy + # end + # end + + # Enables polymorphic Resource Owner association for Access Tokens and Access Grants. + # By default this option is disabled. + # + # Make sure you properly setup you database and have all the required columns (run + # `bundle exec rails generate doorkeeper:enable_polymorphic_resource_owner` and execute Rails + # migrations). + # + # If this option enabled, Doorkeeper will store not only Resource Owner primary key + # value, but also it's type (class name). See "Polymorphic Associations" section of + # Rails guides: https://guides.rubyonrails.org/association_basics.html#polymorphic-associations + # + # [NOTE] If you apply this option on already existing project don't forget to manually + # update `resource_owner_type` column in the database and fix migration template as it will + # set NOT NULL constraint for Access Grants table. + # + # use_polymorphic_resource_owner + + # If you are planning to use Doorkeeper in Rails 5 API-only application, then you might + # want to use API mode that will skip all the views management and change the way how + # Doorkeeper responds to a requests. + # + # api_only + + # Enforce token request content type to application/x-www-form-urlencoded. + # It is not enabled by default to not break prior versions of the gem. + # + # enforce_content_type + + # Authorization Code expiration time (default: 10 minutes). + # + # authorization_code_expires_in 10.minutes + + # Access token expiration time (default: 2 hours). + # If you want to disable expiration, set this to `nil`. + # + # access_token_expires_in 2.hours + + # Assign custom TTL for access tokens. Will be used instead of access_token_expires_in + # option if defined. In case the block returns `nil` value Doorkeeper fallbacks to + # +access_token_expires_in+ configuration option value. If you really need to issue a + # non-expiring access token (which is not recommended) then you need to return + # Float::INFINITY from this block. + # + # `context` has the following properties available: + # + # * `client` - the OAuth client application (see Doorkeeper::OAuth::Client) + # * `grant_type` - the grant type of the request (see Doorkeeper::OAuth) + # * `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes) + # * `resource_owner` - authorized resource owner instance (if present) + # + # custom_access_token_expires_in do |context| + # context.client.additional_settings.implicit_oauth_expiration + # end + + # Use a custom class for generating the access token. + # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-access-token-generator + # + # access_token_generator '::Doorkeeper::JWT' + + # The controller +Doorkeeper::ApplicationController+ inherits from. + # Defaults to +ActionController::Base+ unless +api_only+ is set, which changes the default to + # +ActionController::API+. The return value of this option must be a stringified class name. + # See https://doorkeeper.gitbook.io/guides/configuration/other-configurations#custom-controllers + # + # base_controller 'ApplicationController' + + # Reuse access token for the same resource owner within an application (disabled by default). + # + # This option protects your application from creating new tokens before old **valid** one becomes + # expired so your database doesn't bloat. Keep in mind that when this option is enabled Doorkeeper + # doesn't update existing token expiration time, it will create a new token instead if no active matching + # token found for the application, resources owner and/or set of scopes. + # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/383 + # + # You can not enable this option together with +hash_token_secrets+. + # + # reuse_access_token + + # In case you enabled `reuse_access_token` option Doorkeeper will try to find matching + # token using `matching_token_for` Access Token API that searches for valid records + # in batches in order not to pollute the memory with all the database records. By default + # Doorkeeper uses batch size of 10 000 records. You can increase or decrease this value + # depending on your needs and server capabilities. + # + # token_lookup_batch_size 10_000 + + # Set a limit for token_reuse if using reuse_access_token option + # + # This option limits token_reusability to some extent. + # If not set then access_token will be reused unless it expires. + # Rationale: https://github.com/doorkeeper-gem/doorkeeper/issues/1189 + # + # This option should be a percentage(i.e. (0,100]) + # + # token_reuse_limit 100 + + # Only allow one valid access token obtained via client credentials + # per client. If a new access token is obtained before the old one + # expired, the old one gets revoked (disabled by default) + # + # When enabling this option, make sure that you do not expect multiple processes + # using the same credentials at the same time (e.g. web servers spanning + # multiple machines and/or processes). + # + # revoke_previous_client_credentials_token + + # Hash access and refresh tokens before persisting them. + # This will disable the possibility to use +reuse_access_token+ + # since plain values can no longer be retrieved. + # + # Note: If you are already a user of doorkeeper and have existing tokens + # in your installation, they will be invalid without adding 'fallback: :plain'. + # + # hash_token_secrets + # By default, token secrets will be hashed using the + # +Doorkeeper::Hashing::SHA256+ strategy. + # + # If you wish to use another hashing implementation, you can override + # this strategy as follows: + # + # hash_token_secrets using: '::Doorkeeper::Hashing::MyCustomHashImpl' + # + # Keep in mind that changing the hashing function will invalidate all existing + # secrets, if there are any. + + # Hash application secrets before persisting them. + # + # hash_application_secrets + # + # By default, applications will be hashed + # with the +Doorkeeper::SecretStoring::SHA256+ strategy. + # + # If you wish to use bcrypt for application secret hashing, uncomment + # this line instead: + # + # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt' + + # When the above option is enabled, and a hashed token or secret is not found, + # you can allow to fall back to another strategy. For users upgrading + # doorkeeper and wishing to enable hashing, you will probably want to enable + # the fallback to plain tokens. + # + # This will ensure that old access tokens and secrets + # will remain valid even if the hashing above is enabled. + # + # This can be done by adding 'fallback: plain', e.g. : + # + # hash_application_secrets using: '::Doorkeeper::SecretStoring::BCrypt', fallback: :plain + + # Issue access tokens with refresh token (disabled by default), you may also + # pass a block which accepts `context` to customize when to give a refresh + # token or not. Similar to +custom_access_token_expires_in+, `context` has + # the following properties: + # + # `client` - the OAuth client application (see Doorkeeper::OAuth::Client) + # `grant_type` - the grant type of the request (see Doorkeeper::OAuth) + # `scopes` - the requested scopes (see Doorkeeper::OAuth::Scopes) + # + # use_refresh_token + + # Provide support for an owner to be assigned to each registered application (disabled by default) + # Optional parameter confirmation: true (default: false) if you want to enforce ownership of + # a registered application + # NOTE: you must also run the rails g doorkeeper:application_owner generator + # to provide the necessary support + # + # enable_application_owner confirmation: false + + # Define access token scopes for your provider + # For more information go to + # https://doorkeeper.gitbook.io/guides/ruby-on-rails/scopes + # + # default_scopes :public + # optional_scopes :write, :update + + # Allows to restrict only certain scopes for grant_type. + # By default, all the scopes will be available for all the grant types. + # + # Keys to this hash should be the name of grant_type and + # values should be the array of scopes for that grant type. + # Note: scopes should be from configured_scopes (i.e. default or optional) + # + # scopes_by_grant_type password: [:write], client_credentials: [:update] + + # Forbids creating/updating applications with arbitrary scopes that are + # not in configuration, i.e. +default_scopes+ or +optional_scopes+. + # (disabled by default) + # + # enforce_configured_scopes + + # Change the way client credentials are retrieved from the request object. + # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then + # falls back to the `:client_id` and `:client_secret` params from the `params` object. + # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated + # for more information on customization + # + # client_credentials :from_basic, :from_params + + # Change the way access token is authenticated from the request object. + # By default it retrieves first from the `HTTP_AUTHORIZATION` header, then + # falls back to the `:access_token` or `:bearer_token` params from the `params` object. + # Check out https://github.com/doorkeeper-gem/doorkeeper/wiki/Changing-how-clients-are-authenticated + # for more information on customization + # + # access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param + + # Forces the usage of the HTTPS protocol in non-native redirect uris (enabled + # by default in non-development environments). OAuth2 delegates security in + # communication to the HTTPS protocol so it is wise to keep this enabled. + # + # Callable objects such as proc, lambda, block or any object that responds to + # #call can be used in order to allow conditional checks (to allow non-SSL + # redirects to localhost for example). + # + # force_ssl_in_redirect_uri !Rails.env.development? + # + # force_ssl_in_redirect_uri { |uri| uri.host != 'localhost' } + + # Specify what redirect URI's you want to block during Application creation. + # Any redirect URI is allowed by default. + # + # You can use this option in order to forbid URI's with 'javascript' scheme + # for example. + # + # forbid_redirect_uri { |uri| uri.scheme.to_s.downcase == 'javascript' } + + # Allows to set blank redirect URIs for Applications in case Doorkeeper configured + # to use URI-less OAuth grant flows like Client Credentials or Resource Owner + # Password Credentials. The option is on by default and checks configured grant + # types, but you **need** to manually drop `NOT NULL` constraint from `redirect_uri` + # column for `oauth_applications` database table. + # + # You can completely disable this feature with: + # + # allow_blank_redirect_uri false + # + # Or you can define your custom check: + # + # allow_blank_redirect_uri do |grant_flows, client| + # client.superapp? + # end + + # Specify how authorization errors should be handled. + # By default, doorkeeper renders json errors when access token + # is invalid, expired, revoked or has invalid scopes. + # + # If you want to render error response yourself (i.e. rescue exceptions), + # set +handle_auth_errors+ to `:raise` and rescue Doorkeeper::Errors::InvalidToken + # or following specific errors: + # + # Doorkeeper::Errors::TokenForbidden, Doorkeeper::Errors::TokenExpired, + # Doorkeeper::Errors::TokenRevoked, Doorkeeper::Errors::TokenUnknown + # + # handle_auth_errors :raise + + # Customize token introspection response. + # Allows to add your own fields to default one that are required by the OAuth spec + # for the introspection response. It could be `sub`, `aud` and so on. + # This configuration option can be a proc, lambda or any Ruby object responds + # to `.call` method and result of it's invocation must be a Hash. + # + # custom_introspection_response do |token, context| + # { + # "sub": "Z5O3upPC88QrAjx00dis", + # "aud": "https://protected.example.net/resource", + # "username": User.find(token.resource_owner_id).username + # } + # end + # + # or + # + # custom_introspection_response CustomIntrospectionResponder + + # Specify what grant flows are enabled in array of Strings. The valid + # strings and the flows they enable are: + # + # "authorization_code" => Authorization Code Grant Flow + # "implicit" => Implicit Grant Flow + # "password" => Resource Owner Password Credentials Grant Flow + # "client_credentials" => Client Credentials Grant Flow + # + # If not specified, Doorkeeper enables authorization_code and + # client_credentials. + # + # implicit and password grant flows have risks that you should understand + # before enabling: + # https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.2 + # https://datatracker.ietf.org/doc/html/rfc6819#section-4.4.3 + # + # grant_flows %w[authorization_code client_credentials] + + # Allows to customize OAuth grant flows that +each+ application support. + # You can configure a custom block (or use a class respond to `#call`) that must + # return `true` in case Application instance supports requested OAuth grant flow + # during the authorization request to the server. This configuration +doesn't+ + # set flows per application, it only allows to check if application supports + # specific grant flow. + # + # For example you can add an additional database column to `oauth_applications` table, + # say `t.array :grant_flows, default: []`, and store allowed grant flows that can + # be used with this application there. Then when authorization requested Doorkeeper + # will call this block to check if specific Application (passed with client_id and/or + # client_secret) is allowed to perform the request for the specific grant type + # (authorization, password, client_credentials, etc). + # + # Example of the block: + # + # ->(flow, client) { client.grant_flows.include?(flow) } + # + # In case this option invocation result is `false`, Doorkeeper server returns + # :unauthorized_client error and stops the request. + # + # @param allow_grant_flow_for_client [Proc] Block or any object respond to #call + # @return [Boolean] `true` if allow or `false` if forbid the request + # + # allow_grant_flow_for_client do |grant_flow, client| + # # `grant_flows` is an Array column with grant + # # flows that application supports + # + # client.grant_flows.include?(grant_flow) + # end + + # If you need arbitrary Resource Owner-Client authorization you can enable this option + # and implement the check your need. Config option must respond to #call and return + # true in case resource owner authorized for the specific application or false in other + # cases. + # + # Be default all Resource Owners are authorized to any Client (application). + # + # authorize_resource_owner_for_client do |client, resource_owner| + # resource_owner.admin? || client.owners_allowlist.include?(resource_owner) + # end + + # Hook into the strategies' request & response life-cycle in case your + # application needs advanced customization or logging: + # + # before_successful_strategy_response do |request| + # puts "BEFORE HOOK FIRED! #{request}" + # end + # + # after_successful_strategy_response do |request, response| + # puts "AFTER HOOK FIRED! #{request}, #{response}" + # end + + # Hook into Authorization flow in order to implement Single Sign Out + # or add any other functionality. Inside the block you have an access + # to `controller` (authorizations controller instance) and `context` + # (Doorkeeper::OAuth::Hooks::Context instance) which provides pre auth + # or auth objects with issued token based on hook type (before or after). + # + # before_successful_authorization do |controller, context| + # Rails.logger.info(controller.request.params.inspect) + # + # Rails.logger.info(context.pre_auth.inspect) + # end + # + # after_successful_authorization do |controller, context| + # controller.session[:logout_urls] << + # Doorkeeper::Application + # .find_by(controller.request.params.slice(:redirect_uri)) + # .logout_uri + # + # Rails.logger.info(context.auth.inspect) + # Rails.logger.info(context.issued_token) + # end + + # Under some circumstances you might want to have applications auto-approved, + # so that the user skips the authorization step. + # For example if dealing with a trusted application. + # + # skip_authorization do |resource_owner, client| + # client.superapp? or resource_owner.admin? + # end + + # Configure custom constraints for the Token Introspection request. + # By default this configuration option allows to introspect a token by another + # token of the same application, OR to introspect the token that belongs to + # authorized client (from authenticated client) OR when token doesn't + # belong to any client (public token). Otherwise requester has no access to the + # introspection and it will return response as stated in the RFC. + # + # Block arguments: + # + # @param token [Doorkeeper::AccessToken] + # token to be introspected + # + # @param authorized_client [Doorkeeper::Application] + # authorized client (if request is authorized using Basic auth with + # Client Credentials for example) + # + # @param authorized_token [Doorkeeper::AccessToken] + # Bearer token used to authorize the request + # + # In case the block returns `nil` or `false` introspection responses with 401 status code + # when using authorized token to introspect, or you'll get 200 with { "active": false } body + # when using authorized client to introspect as stated in the + # RFC 7662 section 2.2. Introspection Response. + # + # Using with caution: + # Keep in mind that these three parameters pass to block can be nil as following case: + # `authorized_client` is nil if and only if `authorized_token` is present, and vice versa. + # `token` will be nil if and only if `authorized_token` is present. + # So remember to use `&` or check if it is present before calling method on + # them to make sure you doesn't get NoMethodError exception. + # + # You can define your custom check: + # + # allow_token_introspection do |token, authorized_client, authorized_token| + # if authorized_token + # # customize: require `introspection` scope + # authorized_token.application == token&.application || + # authorized_token.scopes.include?("introspection") + # elsif token.application + # # `protected_resource` is a new database boolean column, for example + # authorized_client == token.application || authorized_client.protected_resource? + # else + # # public token (when token.application is nil, token doesn't belong to any application) + # true + # end + # end + # + # Or you can completely disable any token introspection: + # + # allow_token_introspection false + # + # If you need to block the request at all, then configure your routes.rb or web-server + # like nginx to forbid the request. + + # WWW-Authenticate Realm (default: "Doorkeeper"). + # + # realm "Doorkeeper" +end diff --git a/decidim-erc-crm_authenticable.gemspec b/decidim-erc-crm_authenticable.gemspec index d6bd75c..d43c3ef 100644 --- a/decidim-erc-crm_authenticable.gemspec +++ b/decidim-erc-crm_authenticable.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new do |s| s.email = ["isaac.mg@coditramuntana.com"] s.license = "AGPL-3.0" s.homepage = "https://github.com/CodiTramuntana/decidim-erc-crm_authenticable/" - s.required_ruby_version = ">= 2.6.3" + s.required_ruby_version = ">= 2.7.2" s.name = "decidim-erc-crm_authenticable" s.summary = "A decidim Erc::CrmAuthenticable module" @@ -18,8 +18,8 @@ Gem::Specification.new do |s| s.files = Dir["{app,config,lib}/**/*", "LICENSE-AGPLv3.txt", "Rakefile", "README.md"] - s.add_dependency "decidim-core", Decidim::Erc::CrmAuthenticable.decidim_version - s.add_dependency "decidim-verifications", Decidim::Erc::CrmAuthenticable.decidim_version + s.add_dependency "decidim-core", "~>#{Decidim::Erc::CrmAuthenticable.decidim_version}" + s.add_dependency "decidim-verifications", "~>#{Decidim::Erc::CrmAuthenticable.decidim_version}" - s.add_development_dependency "decidim-dev", Decidim::Erc::CrmAuthenticable.decidim_version + s.add_development_dependency "decidim-dev", "~>#{Decidim::Erc::CrmAuthenticable.decidim_version}" end diff --git a/lib/decidim/erc/crm_authenticable/engine.rb b/lib/decidim/erc/crm_authenticable/engine.rb index b27e842..e548937 100644 --- a/lib/decidim/erc/crm_authenticable/engine.rb +++ b/lib/decidim/erc/crm_authenticable/engine.rb @@ -28,7 +28,10 @@ class Engine < ::Rails::Engine # make decorators available to applications that use this Engine config.to_prepare do - Dir.glob(Decidim::Erc::CrmAuthenticable::Engine.root + "app/decorators/**/*_decorator*.rb").each do |c| + Dir.glob(File.join( + Decidim::Erc::CrmAuthenticable::Engine.root, + "app/decorators/**/*_decorator*.rb" + )).each do |c| require_dependency(c) end end diff --git a/lib/decidim/erc/crm_authenticable/version.rb b/lib/decidim/erc/crm_authenticable/version.rb index 5f645a0..4083fc1 100644 --- a/lib/decidim/erc/crm_authenticable/version.rb +++ b/lib/decidim/erc/crm_authenticable/version.rb @@ -4,11 +4,11 @@ module Decidim module Erc module CrmAuthenticable def self.version - "1.1.6" + "1.2.0" end def self.decidim_version - "0.23.6" + "0.24.0" end end end diff --git a/spec/lib/decidim/erc/crm_authenticable_spec.rb b/spec/lib/decidim/erc/crm_authenticable_spec.rb index bda8439..a8c20c1 100644 --- a/spec/lib/decidim/erc/crm_authenticable_spec.rb +++ b/spec/lib/decidim/erc/crm_authenticable_spec.rb @@ -7,7 +7,7 @@ module Erc describe CrmAuthenticable do context "when the `users_csv_path` is set" do before do - Rails.application.secrets.erc_crm_authenticable[:users_csv_path] = "spec/fixtures/files/csv_users_pre.csv" + Rails.application.secrets.erc_crm_authenticable[:users_csv_path] = "spec/fixtures/files/csv_users.csv" end after do diff --git a/spec/services/erc/crm_authenticable/civi_crm_client_spec.rb b/spec/services/erc/crm_authenticable/civi_crm_client_spec.rb index 6818f5c..b20a7c9 100644 --- a/spec/services/erc/crm_authenticable/civi_crm_client_spec.rb +++ b/spec/services/erc/crm_authenticable/civi_crm_client_spec.rb @@ -35,7 +35,7 @@ module CrmAuthenticable context "when it fails to connect to CiviCRM" do before do - expect(RestClient).to receive(:get).and_return(RestClient::ImATeapot) + allow(RestClient).to receive(:get).and_return(RestClient::ImATeapot) end it { is_expected.to include(error: true) } diff --git a/spec/services/erc/crm_authenticable/crm_authenticable_authorization_handler_spec.rb b/spec/services/erc/crm_authenticable/crm_authenticable_authorization_handler_spec.rb index a74d769..693a477 100644 --- a/spec/services/erc/crm_authenticable/crm_authenticable_authorization_handler_spec.rb +++ b/spec/services/erc/crm_authenticable/crm_authenticable_authorization_handler_spec.rb @@ -62,7 +62,7 @@ module CrmAuthenticable before do allow(::Decidim::Erc::CrmAuthenticable).to receive(:csv_mode?).and_return(true) - Rails.application.secrets.erc_crm_authenticable[:users_csv_path] = "spec/fixtures/files/csv_users_pre.csv" + Rails.application.secrets.erc_crm_authenticable[:users_csv_path] = "spec/fixtures/files/csv_users.csv" end after do diff --git a/spec/system/decidim/erc/crm_authenticable/crm_authenticable_action_authorizer_spec.rb b/spec/system/decidim/erc/crm_authenticable/crm_authenticable_action_authorizer_spec.rb index 03ccee4..070b3f2 100644 --- a/spec/system/decidim/erc/crm_authenticable/crm_authenticable_action_authorizer_spec.rb +++ b/spec/system/decidim/erc/crm_authenticable/crm_authenticable_action_authorizer_spec.rb @@ -25,11 +25,13 @@ def answer_survey within "#edit_questionnaire_#{survey.questionnaire.id}" do - fill_in :questionnaire_answers_0, with: "NS/NC" + fill_in :questionnaire_responses_0, with: "NS/NC" check :questionnaire_tos_agreement click_button "Submit" end - page.driver.browser.switch_to.alert.accept + within ".confirm-modal-footer" do + click_on "OK" + end end def to_strftime(date) diff --git a/spec/system/decidim/erc/crm_authenticable/registration_spec.rb b/spec/system/decidim/erc/crm_authenticable/registration_spec.rb index f7f0b65..d8b9917 100644 --- a/spec/system/decidim/erc/crm_authenticable/registration_spec.rb +++ b/spec/system/decidim/erc/crm_authenticable/registration_spec.rb @@ -9,14 +9,7 @@ before do switch_to_host(organization.host) - secrets = Rails.application.secrets - allow(Rails.application).to receive(:secrets).and_return( - secrets.merge( - erc_crm_authenticable: { - users_csv_path: "spec/fixtures/files/csv_users.csv" - } - ) - ) + Rails.application.secrets end context "when the user signs up" do @@ -29,7 +22,7 @@ within "#register-form-step-1" do expect(page).to have_field("user_document_number") end - expect(page).to have_button("Request verification") + expect(page).to have_button("Request verification", visible: :all) end context "when the 'Identity document form' is filled with valid data" do @@ -38,7 +31,7 @@ within "#register-form-step-1" do fill_in :user_document_number, with: "123456789A" end - click_button "Request verification" + click_button "Request verification", visible: :all end it "redirects to the 'Registration' page" do @@ -49,7 +42,7 @@ it "prefilles the 'Registration form' with data from CiviCRM" do within "#register-form-step-2" do # Data returned from CiviCRM should be readonly and users must be informed. - expect(page).to have_tag("div.callout.warning", text: /administracio@esquerra.cat/) + expect(page).to have_tag("div.callout.announcement", text: /administracio@esquerra.cat/) expect(page).to have_field("user_name", with: "John Doe", readonly: true) expect(page).to have_field("user_nickname", with: "JD", readonly: true) expect(page).to have_field("user_email", with: "john.doe@example.org", readonly: true) @@ -69,13 +62,6 @@ click_button "Sign up" end - it "newsletter modal show" do - expect(page).to have_css("#sign-up-newsletter-modal") - within "#register-form-step-2" do - check :user_newsleter - end - end - it "registers the user" do expect(page).to have_css(".callout.success", text: "You have signed up successfully.") end @@ -87,7 +73,7 @@ within "#register-form-step-1" do fill_in :user_document_number, with: "123456789A" end - click_button "Request verification" + click_button "Request verification", visible: :all end it "does NOT redirect to the decidim 'Registration' page" do @@ -124,7 +110,7 @@ within "#register-form-step-1" do fill_in :user_document_number, with: "123456789A" end - click_button "Request verification" + click_button "Request verification", visible: :all end it "does NOT redirect to the decidim 'Registration' page" do @@ -144,7 +130,7 @@ within "#register-form-step-1" do fill_in :user_document_number, with: "(╯°□°)╯︵ ┻━┻" end - click_button "Request verification" + click_button "Request verification", visible: :all end it "does NOT redirect to the 'Registration' page" do @@ -161,7 +147,7 @@ within "#register-form-step-1" do fill_in :user_document_number, with: "123456789A" end - click_button "Request verification" + click_button "Request verification", visible: :all end it "does NOT redirect to the decidim 'Registration' page" do @@ -178,7 +164,7 @@ within "#register-form-step-1" do fill_in :user_document_number, with: "123456789A" end - click_button "Request verification" + click_button "Request verification", visible: :all end it "does NOT redirect to the decidim 'Registration' page" do @@ -196,7 +182,7 @@ within "#register-form-step-1" do fill_in :user_document_number, with: "123456789A" end - click_button "Request verification" + click_button "Request verification", visible: :all end it "does NOT redirect to the decidim 'Registration' page" do diff --git a/spec/tasks/civi_crm_spec.rb b/spec/tasks/civi_crm_spec.rb index a5bbe87..3bad63e 100644 --- a/spec/tasks/civi_crm_spec.rb +++ b/spec/tasks/civi_crm_spec.rb @@ -12,7 +12,7 @@ before do Rake.application.rake_require "tasks/civi_crm" Rake::Task.define_task(:environment) - allow(STDOUT).to receive(:puts) + allow($stdout).to receive(:puts) allow(File).to receive(:write) end @@ -60,8 +60,8 @@ it "creates a YAML file with comarcals in it" do expect(File).to receive(:write).with( - %r{config\/civi_crm\/comarcals.yml}, - /'4'\: Vallès Oriental \(comarcal\)/ + %r{config/civi_crm/comarcals.yml}, + /'4': Vallès Oriental \(comarcal\)/ ) invoke_task @@ -88,8 +88,8 @@ it "creates a YAML file with regionals in it" do expect(File).to receive(:write).with( - %r{config\/civi_crm\/regionals.yml}, - /'5827'\: Barcelona \(regional\)/ + %r{config/civi_crm/regionals.yml}, + /'5827': Barcelona \(regional\)/ ) invoke_task @@ -117,8 +117,8 @@ it "creates a YAML file with local_comarcal_relationships in it" do expect(File).to receive(:write).with( - %r{config\/civi_crm\/local_comarcal_relationships.yml}, - /'1'\: '4'/ + %r{config/civi_crm/local_comarcal_relationships.yml}, + /'1': '4'/ ) invoke_task @@ -146,8 +146,8 @@ it "creates a YAML file with local_comarcal_relationships in it" do expect(File).to receive(:write).with( - %r{config\/civi_crm\/local_regional_relationships.yml}, - /'1'\: '5827'/ + %r{config/civi_crm/local_regional_relationships.yml}, + /'1': '5827'/ ) invoke_task @@ -178,7 +178,7 @@ it "creates a YAML file with comarcal_exceptions in it" do expect(File).to receive(:write).with( - %r{config\/civi_crm\/comarcal_exceptions.yml}, + %r{config/civi_crm/comarcal_exceptions.yml}, "---\n'4': Vallès Oriental (comarcal)\n" # Exact match ) @@ -211,14 +211,14 @@ end before do - allow(YAML).to receive(:load_file).with(%r{config\/civi_crm\/comarcal_exceptions.yml}).and_return(comarcal_exceptions) - allow(YAML).to receive(:load_file).with(%r{config\/civi_crm\/local_comarcal_relationships.yml}).and_return(local_comarcal_rel) - allow(YAML).to receive(:load_file).with(%r{config\/civi_crm\/local_regional_relationships.yml}).and_return(local_regional_rel) + allow(YAML).to receive(:load_file).with(%r{config/civi_crm/comarcal_exceptions.yml}).and_return(comarcal_exceptions) + allow(YAML).to receive(:load_file).with(%r{config/civi_crm/local_comarcal_relationships.yml}).and_return(local_comarcal_rel) + allow(YAML).to receive(:load_file).with(%r{config/civi_crm/local_regional_relationships.yml}).and_return(local_regional_rel) end it "creates a YAML file with decidim_scopes_mapping in it" do expect(File).to receive(:write).with( - %r{config\/civi_crm\/decidim_scopes_mapping.yml}, + %r{config/civi_crm/decidim_scopes_mapping.yml}, "---\n'1': '4'\n'2': '3'\n" # Exact match )