diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 10:33:21 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-19 10:33:21 +0300 |
commit | 36a59d088eca61b834191dacea009677a96c052f (patch) | |
tree | e4f33972dab5d8ef79e3944a9f403035fceea43f /lib/atlassian | |
parent | a1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff) |
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'lib/atlassian')
-rw-r--r-- | lib/atlassian/jira_connect/asymmetric_jwt.rb | 68 | ||||
-rw-r--r-- | lib/atlassian/jira_connect/jwt/asymmetric.rb | 80 | ||||
-rw-r--r-- | lib/atlassian/jira_connect/jwt/symmetric.rb | 55 |
3 files changed, 135 insertions, 68 deletions
diff --git a/lib/atlassian/jira_connect/asymmetric_jwt.rb b/lib/atlassian/jira_connect/asymmetric_jwt.rb deleted file mode 100644 index a5668701965..00000000000 --- a/lib/atlassian/jira_connect/asymmetric_jwt.rb +++ /dev/null @@ -1,68 +0,0 @@ -# frozen_string_literal: true - -module Atlassian - module JiraConnect - # See documentation about Atlassian asymmetric JWT verification: - # https://developer.atlassian.com/cloud/jira/platform/understanding-jwt-for-connect-apps/#verifying-a-asymmetric-jwt-token-for-install-callbacks - - class AsymmetricJwt - include Gitlab::Utils::StrongMemoize - - KeyFetchError = Class.new(StandardError) - - ALGORITHM = 'RS256' - PUBLIC_KEY_CDN_URL = 'https://connect-install-keys.atlassian.com/' - UUID4_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/.freeze - - def initialize(token, verification_claims) - @token = token - @verification_claims = verification_claims - end - - def valid? - claims.present? && claims['qsh'] == verification_qsh - end - - def iss_claim - return unless claims - - claims['iss'] - end - - private - - def claims - strong_memoize(:claims) do - _, jwt_headers = decode_token - public_key = retrieve_public_key(jwt_headers['kid']) - decoded_claims, _ = decode_token(public_key, true, **relevant_claims, verify_aud: true, verify_iss: true, algorithm: ALGORITHM) - - decoded_claims - rescue JWT::DecodeError, OpenSSL::PKey::PKeyError, KeyFetchError - end - end - - def decode_token(key = nil, verify = false, **claims) - Atlassian::Jwt.decode(@token, key, verify, **claims) - end - - def retrieve_public_key(key_id) - raise KeyFetchError unless UUID4_REGEX.match?(key_id) - - public_key = Gitlab::HTTP.try_get(PUBLIC_KEY_CDN_URL + key_id).try(:body) - - raise KeyFetchError if public_key.blank? - - OpenSSL::PKey.read(public_key) - end - - def relevant_claims - @verification_claims.slice(:aud, :iss) - end - - def verification_qsh - @verification_claims[:qsh] - end - end - end -end diff --git a/lib/atlassian/jira_connect/jwt/asymmetric.rb b/lib/atlassian/jira_connect/jwt/asymmetric.rb new file mode 100644 index 00000000000..0611a17c005 --- /dev/null +++ b/lib/atlassian/jira_connect/jwt/asymmetric.rb @@ -0,0 +1,80 @@ +# frozen_string_literal: true + +module Atlassian + module JiraConnect + module Jwt + # See documentation about Atlassian asymmetric JWT verification: + # https://developer.atlassian.com/cloud/jira/platform/understanding-jwt-for-connect-apps/#verifying-a-asymmetric-jwt-token-for-install-callbacks + + class Asymmetric + include Gitlab::Utils::StrongMemoize + + KeyFetchError = Class.new(StandardError) + + ALGORITHM = 'RS256' + PUBLIC_KEY_CDN_URL = 'https://connect-install-keys.atlassian.com/' + UUID4_REGEX = /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/.freeze + + def initialize(token, verification_claims) + @token = token + @verification_claims = verification_claims + end + + def valid? + claims.present? && claims['qsh'] == verification_qsh + end + + def iss_claim + return unless claims + + claims['iss'] + end + + private + + def claims + strong_memoize(:claims) do + _, jwt_headers = decode_token + public_key = retrieve_public_key(jwt_headers['kid']) + + decoded_claims(public_key) + rescue JWT::DecodeError, OpenSSL::PKey::PKeyError, KeyFetchError + end + end + + def decoded_claims(public_key) + decode_token( + public_key, + true, + **relevant_claims, + verify_aud: true, + verify_iss: true, + algorithm: ALGORITHM + ).first + end + + def decode_token(key = nil, verify = false, **claims) + Atlassian::Jwt.decode(@token, key, verify, **claims) + end + + def retrieve_public_key(key_id) + raise KeyFetchError unless UUID4_REGEX.match?(key_id) + + public_key = Gitlab::HTTP.try_get(PUBLIC_KEY_CDN_URL + key_id).try(:body) + + raise KeyFetchError if public_key.blank? + + OpenSSL::PKey.read(public_key) + end + + def relevant_claims + @verification_claims.slice(:aud, :iss) + end + + def verification_qsh + @verification_claims[:qsh] + end + end + end + end +end diff --git a/lib/atlassian/jira_connect/jwt/symmetric.rb b/lib/atlassian/jira_connect/jwt/symmetric.rb new file mode 100644 index 00000000000..61e5bd923a4 --- /dev/null +++ b/lib/atlassian/jira_connect/jwt/symmetric.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module Atlassian + module JiraConnect + module Jwt + class Symmetric + include Gitlab::Utils::StrongMemoize + + CONTEXT_QSH_STRING = 'context-qsh' + + def initialize(jwt) + @jwt = jwt + end + + def iss_claim + jwt_headers['iss'] + end + + def sub_claim + jwt_headers['sub'] + end + + def valid?(shared_secret) + Atlassian::Jwt.decode(@jwt, shared_secret).present? + rescue JWT::DecodeError + false + end + + def verify_qsh_claim(url_with_query, method, url) + qsh_claim == Atlassian::Jwt.create_query_string_hash(url_with_query, method, url) + rescue StandardError + false + end + + def verify_context_qsh_claim + qsh_claim == CONTEXT_QSH_STRING + end + + private + + def qsh_claim + jwt_headers['qsh'] + end + + def jwt_headers + strong_memoize(:jwt_headers) do + Atlassian::Jwt.decode(@jwt, nil, false).first + rescue JWT::DecodeError + {} + end + end + end + end + end +end |