decidim-ub 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Decidim::Ub
2
+
3
+ [![[CI] Lint](https://github.com/Platoniq/decidim-module-ub/actions/workflows/lint.yml/badge.svg)](https://github.com/Platoniq/decidim-module-ub/actions/workflows/lint.yml)
4
+ [![[CI] Test](https://github.com/Platoniq/decidim-module-ub/actions/workflows/test.yml/badge.svg)](https://github.com/Platoniq/decidim-module-ub/actions/workflows/test.yml)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/c975a347c58389448503/maintainability)](https://codeclimate.com/github/Platoniq/decidim-module-ub/maintainability)
6
+ [![Coverage Status](https://coveralls.io/repos/github/Platoniq/decidim-module-ub/badge.svg?branch=main)](https://coveralls.io/github/Platoniq/decidim-module-ub?branch=main)
7
+
8
+ A Decidim module to sync users from Universitat de Barcelona who connect to the platform.
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ ```ruby
15
+ gem "decidim-ub", git: "https://github.com/Platoniq/decidim-module-ub"
16
+ ```
17
+
18
+ And then execute:
19
+
20
+ ```bash
21
+ bundle
22
+ bundle exec rails decidim_ub:install:migrations
23
+ bundle exec rails db:migrate
24
+ ```
25
+
26
+ ## Configuration
27
+
28
+ You need to configure some environment variables for the OAuth client:
29
+
30
+ | ENV | Description | Example | Default |
31
+ |------------------|-------------------------------------------|-----------------------------|----------------------------|
32
+ | UB_CLIENT_ID | The OAuth2 client ID | `your-client-id` | |
33
+ | UB_CLIENT_SECRET | The OAuth2 client secret | `your-client-secret` | |
34
+ | UB_SITE | The OAuth2 site | `https://example.org/oauth` | |
35
+ | UB_AUTHORIZE_URL | The path for the authorization URL | `/authorize` | |
36
+ | UB_TOKEN_URL | The path for the token URL | `/token` | |
37
+ | UB_ICON | The path for the icon shown in the button | `media/images/my_icon.svg` | `media/images/ub_logo.svg` |
38
+
39
+ ## Contributing
40
+
41
+ Contributions are welcome !
42
+
43
+ We expect the contributions to follow the [Decidim's contribution guide](https://github.com/decidim/decidim/blob/develop/CONTRIBUTING.adoc).
44
+
45
+ ## Security
46
+
47
+ Security is very important to us. If you have any issue regarding security, please disclose the information responsibly by sending an email to __francisco.bolivar [at] nazaries [dot] com__ and not by creating a Github issue.
48
+
49
+ ## License
50
+
51
+ This engine is distributed under the GNU AFFERO GENERAL PUBLIC LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "decidim/dev/common_rake"
4
+ require "fileutils"
5
+
6
+ def install_module(path)
7
+ Dir.chdir(path) do
8
+ system("bundle exec rake decidim_ub:install:migrations")
9
+ system("bundle exec rake db:migrate")
10
+ end
11
+ end
12
+
13
+ def install_initializer(path, env)
14
+ Dir.chdir(path) do
15
+ FileUtils.cp(
16
+ "#{__dir__}/lib/generators/decidim/app_templates/#{env}/initializer.rb",
17
+ "config/initializers/decidim_ub_config.rb"
18
+ )
19
+ end
20
+ end
21
+
22
+ def seed_db(path)
23
+ Dir.chdir(path) do
24
+ system("bundle exec rake db:seed")
25
+ end
26
+ end
27
+
28
+ desc "Generates a dummy app for testing"
29
+ task test_app: "decidim:generate_external_test_app" do
30
+ ENV["RAILS_ENV"] = "test"
31
+ install_initializer("spec/decidim_dummy_app", "test")
32
+ install_module("spec/decidim_dummy_app")
33
+ end
34
+
35
+ desc "Generates a development app."
36
+ task development_app: "decidim:generate_external_development_app" do
37
+ install_module("development_app")
38
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ class SyncUser < Decidim::Command
6
+ # Public: Initializes the command.
7
+ #
8
+ # user - A decidim user
9
+ # roles - The roles of the user
10
+ def initialize(user, roles)
11
+ @user = user
12
+ @roles = roles
13
+ end
14
+
15
+ # Executes the command. Broadcasts these events:
16
+ #
17
+ # - :ok when everything is valid.
18
+ # - :invalid if we couldn't proceed.
19
+ #
20
+ # Returns nothing.
21
+ def call
22
+ update_user!
23
+ ActiveSupport::Notifications.publish("decidim.ub.user.updated", user.id)
24
+ broadcast(:ok)
25
+ rescue StandardError => e
26
+ broadcast(:invalid, e.message)
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :user, :roles
32
+
33
+ def update_user!
34
+ return unless user.ub_identity?
35
+
36
+ user.ub_roles = roles
37
+ user.save!
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module DeviseAuthenticationMethods
5
+ def first_login_and_not_authorized?(user)
6
+ return false if user.ub_identity?
7
+
8
+ super
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+
5
+ module Decidim
6
+ module Ub
7
+ module Verifications
8
+ class Ub < Decidim::AuthorizationHandler
9
+ validate :user_valid
10
+
11
+ def unique_id
12
+ Digest::SHA512.hexdigest("#{role}/#{uid}-#{Rails.application.secrets.secret_key_base}")
13
+ end
14
+
15
+ protected
16
+
17
+ def organization
18
+ current_organization || user&.organization
19
+ end
20
+
21
+ def uid
22
+ user.ub_identity
23
+ end
24
+
25
+ def user_valid
26
+ errors.add(:user, "decidim.ub.errors.missing_role") unless user.ub_roles.include?(role)
27
+ end
28
+
29
+ def role = raise NotImplementedError
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ module Verifications
6
+ class UbAnt < Ub
7
+ def role = "ANT"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ module Verifications
6
+ class UbEst < Ub
7
+ def role = "EST"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ module Verifications
6
+ class UbPas < Ub
7
+ def role = "PAS"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ module Verifications
6
+ class UbPdi < Ub
7
+ def role = "PDI"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ module Verifications
6
+ class UbPex < Ub
7
+ def role = "PEX"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ module OmniauthHelperOverride
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ alias_method :original_normalize_provider_name, :normalize_provider_name
10
+
11
+ def normalize_provider_name(provider)
12
+ return "Universitat de Barcelona" if provider == :ub
13
+
14
+ original_normalize_provider_name(provider)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ class AutoVerificationJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(user_id)
9
+ @user = Decidim::User.find(user_id)
10
+ @auths = Decidim::Ub.roles_to_auth_name(@user.ub_roles & Decidim::Ub::ROLES)
11
+ update_auths
12
+ rescue ActiveRecord::RecordNotFound
13
+ Rails.logger.error "AutoVerificationJob: ERROR: model not found for user #{user_id}"
14
+ end
15
+
16
+ private
17
+
18
+ def update_auths
19
+ current_auths = user_auths.pluck(:name)
20
+ (current_auths - @auths).each { |name| remove_auth(user_auths.find_by(name:)) }
21
+ (@auths - current_auths).each { |name| create_auth(name) }
22
+ end
23
+
24
+ def create_auth(name)
25
+ return unless (handler = Decidim::AuthorizationHandler.handler_for(name, user: @user))
26
+
27
+ Decidim::Verifications::AuthorizeUser.call(handler, @user.organization) do
28
+ on(:ok) do
29
+ Rails.logger.info "AutoVerificationJob: Success: created auth #{name} for user #{handler.user.id}"
30
+ end
31
+
32
+ on(:invalid) do
33
+ Rails.logger.error "AutoVerificationJob: ERROR: not created auth #{name} for user #{handler.user&.id}"
34
+ end
35
+ end
36
+ end
37
+
38
+ def remove_auth(auth)
39
+ Decidim::Verifications::DestroyUserAuthorization.call(auth) do
40
+ on(:ok) do
41
+ Rails.logger.info "AutoVerificationJob: Success: removed auth #{auth.name} for user #{auth.user.id}"
42
+ end
43
+
44
+ on(:invalid) do
45
+ Rails.logger.error "AutoVerificationJob: ERROR: not removed auth #{auth.name} for user #{auth.user&.id}"
46
+ end
47
+ end
48
+ end
49
+
50
+ def user_auths
51
+ @user_auths ||= Decidim::Authorization.where(user: @user, name: Decidim::Ub.authorizations)
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ class OmniauthUserSyncJob < ApplicationJob
6
+ queue_as :default
7
+
8
+ def perform(data)
9
+ user = Decidim::User.find(data[:user_id])
10
+ return unless user.ub_identity?
11
+
12
+ Decidim::Ub::SyncUser.call(user, data.dig(:raw_data, :info, :roles)) do
13
+ on(:ok) do
14
+ Rails.logger.info "OmniauthUserSyncJob: Success: Ub roles updated for user #{user.id}"
15
+ end
16
+
17
+ on(:invalid) do |message|
18
+ Rails.logger.error "OmniauthUserSyncJob: ERROR: Error updating ub roles '#{message}'"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Decidim
4
+ module Ub
5
+ module UserOverride
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ def ub_identity
10
+ identities.find_by(provider: Decidim::Ub::OMNIAUTH_PROVIDER_NAME)
11
+ end
12
+
13
+ def ub_identity?
14
+ identities.exists?(provider: Decidim::Ub::OMNIAUTH_PROVIDER_NAME)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,2 @@
1
+ // Images
2
+ require.context("../images", true)