From 0b96f20cd70522ac49fbb35ee3fc1d1be8381e3d Mon Sep 17 00:00:00 2001 From: Dario Date: Sun, 18 Aug 2024 21:19:01 +0200 Subject: [PATCH] implement identity provider to allow loading of identities and fix some issues --- lib/code0/identities.rb | 1 + lib/code0/identities/identity_provider.rb | 31 +++++++++++++++++++ lib/code0/identities/provider/base_oauth.rb | 19 ++++++++---- lib/code0/identities/provider/discord.rb | 10 +----- lib/code0/identities/provider/github.rb | 13 ++------ lib/code0/identities/provider/gitlab.rb | 16 +++------- lib/code0/identities/provider/google.rb | 8 ++--- lib/code0/identities/provider/microsoft.rb | 9 +----- .../identities/identity_provider_spec.rb | 24 ++++++++++++++ .../code0/identities/provider/discord_spec.rb | 2 +- 10 files changed, 81 insertions(+), 52 deletions(-) create mode 100644 lib/code0/identities/identity_provider.rb create mode 100644 spec/code0/identities/identity_provider_spec.rb diff --git a/lib/code0/identities.rb b/lib/code0/identities.rb index 6ae1a20..1d397cf 100644 --- a/lib/code0/identities.rb +++ b/lib/code0/identities.rb @@ -3,6 +3,7 @@ require "httparty" require_relative "identities/version" +require_relative "identities/identity_provider" require_relative "identities/identity" require_relative "identities/provider/base_oauth" require_relative "identities/provider/microsoft" diff --git a/lib/code0/identities/identity_provider.rb b/lib/code0/identities/identity_provider.rb new file mode 100644 index 0000000..329ed58 --- /dev/null +++ b/lib/code0/identities/identity_provider.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Code0 + module Identities + class IdentityProvider + attr_reader :providers + + def initialize + @providers = {} + end + + def add_provider(provider_type, config_loader) + add_named_provider provider_type, provider_type, config_loader + end + + def add_named_provider(provider_id, provider_type, config_loader) + provider = Identities::Provider.const_get(provider_type.capitalize).new(config_loader) + providers[provider_id] = provider + end + + def load_identity(provider_id, params) + provider = providers[provider_id] + if provider.nil? + raise Error, "Provider with id '#{provider_id}' is not configured, did you forget to use add_provider" + end + + provider.load_identity(params) + end + end + end +end diff --git a/lib/code0/identities/provider/base_oauth.rb b/lib/code0/identities/provider/base_oauth.rb index e8d5209..e2681e8 100644 --- a/lib/code0/identities/provider/base_oauth.rb +++ b/lib/code0/identities/provider/base_oauth.rb @@ -1,7 +1,14 @@ +# frozen_string_literal: true + module Code0 module Identities module Provider class BaseOauth + attr_reader :config_loader + + def initialize(config_loader) + @config_loader = config_loader + end def authorization_url raise NotImplementedError @@ -19,8 +26,8 @@ def token_payload(code) raise NotImplementedError end - - def load_identity(code) + def load_identity(**params) + code = params[:code] token, token_type = access_token code response = HTTParty.get(user_details_url, @@ -39,9 +46,9 @@ def load_identity(code) def access_token(code) response = HTTParty.post(token_url, body: URI.encode_www_form(token_payload(code)), headers: { - "Content-Type" => "application/x-www-form-urlencoded", - "Accept" => "application/json" - }) + "Content-Type" => "application/x-www-form-urlencoded", + "Accept" => "application/json" + }) check_response response @@ -62,4 +69,4 @@ def create_identity(*) end end end -end \ No newline at end of file +end diff --git a/lib/code0/identities/provider/discord.rb b/lib/code0/identities/provider/discord.rb index 3f882dc..c535828 100644 --- a/lib/code0/identities/provider/discord.rb +++ b/lib/code0/identities/provider/discord.rb @@ -4,12 +4,6 @@ module Code0 module Identities module Provider class Discord < BaseOauth - attr_reader :config_loader - - def initialize(config_loader) - @config_loader = config_loader - end - def token_url "https://discord.com/api/oauth2/token" end @@ -20,8 +14,7 @@ def token_payload(code) grant_type: "authorization_code", redirect_uri: config[:redirect_uri], client_id: config[:client_id], - client_secret: config[:client_secret] - } + client_secret: config[:client_secret] } end def user_details_url @@ -33,7 +26,6 @@ def authorization_url "https://discord.com/oauth2/authorize?client_id=#{config[:client_id]}&response_type=code&redirect_uri=#{URI.encode_uri_component(config[:redirect_uri])}&scope=identify+openid+email" end - def create_identity(response, *) body = response.parsed_response diff --git a/lib/code0/identities/provider/github.rb b/lib/code0/identities/provider/github.rb index a8fea57..057f165 100644 --- a/lib/code0/identities/provider/github.rb +++ b/lib/code0/identities/provider/github.rb @@ -4,12 +4,6 @@ module Code0 module Identities module Provider class Github < BaseOauth - attr_reader :config_loader - - def initialize(config_loader) - @config_loader = config_loader - end - def token_url "https://github.com/login/oauth/access_token" end @@ -19,8 +13,7 @@ def token_payload(code) { code: code, redirect_uri: config[:redirect_uri], client_id: config[:client_id], - client_secret: config[:client_secret] - } + client_secret: config[:client_secret] } end def user_details_url @@ -33,7 +26,7 @@ def authorization_url end def private_email(access_token, token_type) - response = HTTParty.get(user_details_url + "/emails", + response = HTTParty.get("#{user_details_url}/emails", headers: { Authorization: "#{token_type} #{access_token}", "Accept" => "application/json" @@ -52,8 +45,6 @@ def create_identity(response, access_token, token_type) email = private_email(access_token, token_type) if email.nil? - - Identity.new(:github, identifier, username, email, nil, nil) end end diff --git a/lib/code0/identities/provider/gitlab.rb b/lib/code0/identities/provider/gitlab.rb index e1459cd..ca37920 100644 --- a/lib/code0/identities/provider/gitlab.rb +++ b/lib/code0/identities/provider/gitlab.rb @@ -4,19 +4,13 @@ module Code0 module Identities module Provider class Gitlab < BaseOauth - attr_reader :config_loader - - def initialize(config_loader) - @config_loader = config_loader - end - def base_url config = config_loader.call config[:base_url] end def token_url - base_url + "/oauth/token" + "#{base_url}/oauth/token" end def token_payload(code) @@ -25,20 +19,20 @@ def token_payload(code) grant_type: "authorization_code", redirect_uri: config[:redirect_uri], client_id: config[:client_id], - client_secret: config[:client_secret] - } + client_secret: config[:client_secret] } end def user_details_url - base_url + "/api/v4/user" + "#{base_url}/api/v4/user" end def authorization_url config = config_loader.call + # rubocop:disable Layout/LineLength base_url + "/oauth/authorize?client_id=#{config[:client_id]}&response_type=code&redirect_uri=#{URI.encode_uri_component(config[:redirect_uri])}&scope=read_user" + # rubocop:enable Layout/LineLength end - def create_identity(response, *) body = response.parsed_response diff --git a/lib/code0/identities/provider/google.rb b/lib/code0/identities/provider/google.rb index a3890af..82247e7 100644 --- a/lib/code0/identities/provider/google.rb +++ b/lib/code0/identities/provider/google.rb @@ -4,12 +4,6 @@ module Code0 module Identities module Provider class Google < BaseOauth - attr_reader :config_loader - - def initialize(config_loader) - @config_loader = config_loader - end - def base_url "https://accounts.google.com" end @@ -35,7 +29,9 @@ def user_details_url def authorization_url config = config_loader.call + # rubocop:disable Layout/LineLength base_url + "/o/oauth2/v2/auth?client_id=#{config[:client_id]}&response_type=code&redirect_uri=#{URI.encode_www_form_component(config[:redirect_uri])}&scope=openid%20email%20profile" + # rubocop:enable Layout/LineLength end def create_identity(response, *) diff --git a/lib/code0/identities/provider/microsoft.rb b/lib/code0/identities/provider/microsoft.rb index fcf08f9..348329d 100644 --- a/lib/code0/identities/provider/microsoft.rb +++ b/lib/code0/identities/provider/microsoft.rb @@ -4,12 +4,6 @@ module Code0 module Identities module Provider class Microsoft < BaseOauth - attr_reader :config_loader - - def initialize(config_loader) - @config_loader = config_loader - end - def base_url "https://graph.microsoft.com/" end @@ -24,8 +18,7 @@ def token_payload(code) grant_type: "authorization_code", redirect_uri: config[:redirect_uri], client_id: config[:client_id], - client_secret: config[:client_secret] - } + client_secret: config[:client_secret] } end def user_details_url diff --git a/spec/code0/identities/identity_provider_spec.rb b/spec/code0/identities/identity_provider_spec.rb new file mode 100644 index 0000000..d92ab9b --- /dev/null +++ b/spec/code0/identities/identity_provider_spec.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +RSpec.describe Code0::Identities::IdentityProvider do + let(:instance) do + described_class.new + end + + describe "#add_provider" do + it "adds the correct class" do + instance.add_provider :google, -> {} + expect(instance.providers).to match(google: an_instance_of(Code0::Identities::Provider::Google)) + end + end + + describe "#load_identity" do + it "calls the right provider" do + provider = Code0::Identities::Provider::Google.new(-> {}) + allow(provider).to receive(:load_identity) + instance.providers[:google] = provider + instance.load_identity(:google, { test: 1 }) + expect(provider).to have_received(:load_identity).with({ test: 1 }) + end + end +end diff --git a/spec/code0/identities/provider/discord_spec.rb b/spec/code0/identities/provider/discord_spec.rb index 708eafa..5a85ff9 100644 --- a/spec/code0/identities/provider/discord_spec.rb +++ b/spec/code0/identities/provider/discord_spec.rb @@ -8,7 +8,7 @@ client_id: client_id, client_secret: client_secret } - }).load_identity(code) + }).load_identity(code: code) end let(:redirect_uri) { SecureRandom.hex }