Class: SignIn::AssertionValidator

Inherits:
Object
  • Object
show all
Defined in:
app/services/sign_in/assertion_validator.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(assertion:) ⇒ AssertionValidator

Returns a new instance of AssertionValidator.



7
8
9
# File 'app/services/sign_in/assertion_validator.rb', line 7

def initialize(assertion:)
  @assertion = assertion
end

Instance Attribute Details

#assertionObject (readonly)

Returns the value of attribute assertion.



5
6
7
# File 'app/services/sign_in/assertion_validator.rb', line 5

def assertion
  @assertion
end

Instance Method Details

#assertion_audienceObject (private)



125
126
127
# File 'app/services/sign_in/assertion_validator.rb', line 125

def assertion_audience
  @assertion_audience ||= Array(decoded_assertion.aud)
end

#audienceObject (private)



129
130
131
# File 'app/services/sign_in/assertion_validator.rb', line 129

def audience
  @audience ||= .access_token_audience
end

#create_new_access_tokenObject (private)



73
74
75
76
77
78
79
# File 'app/services/sign_in/assertion_validator.rb', line 73

def create_new_access_token
  ServiceAccountAccessToken.new(service_account_id:,
                                audience:,
                                scopes:,
                                user_attributes:,
                                user_identifier:)
end

#decoded_assertionObject (private)



97
98
99
# File 'app/services/sign_in/assertion_validator.rb', line 97

def decoded_assertion
  @decoded_assertion ||= jwt_decode
end

#decoded_assertion_scopes_are_defined_in_config?Boolean (private)

Returns:

  • (Boolean)


81
82
83
84
85
# File 'app/services/sign_in/assertion_validator.rb', line 81

def decoded_assertion_scopes_are_defined_in_config?
  return .scopes.blank? if scopes.blank?

  (scopes - .scopes).empty?
end

#decoded_assertion_without_validationObject (private)



101
102
103
# File 'app/services/sign_in/assertion_validator.rb', line 101

def decoded_assertion_without_validation
  @decoded_assertion_without_validation ||= jwt_decode(with_validation: false)
end

#expirationObject (private)



137
138
139
# File 'app/services/sign_in/assertion_validator.rb', line 137

def expiration
  @expiration ||= decoded_assertion.exp
end

#hostnameObject (private)



91
92
93
94
95
# File 'app/services/sign_in/assertion_validator.rb', line 91

def hostname
  return localhost_hostname if Settings.vsp_environment == 'localhost'

  "https://#{Settings.hostname}"
end

#issued_at_timeObject (private)



133
134
135
# File 'app/services/sign_in/assertion_validator.rb', line 133

def issued_at_time
  @issued_at_time ||= decoded_assertion.iat
end

#issuerObject (private)



121
122
123
# File 'app/services/sign_in/assertion_validator.rb', line 121

def issuer
  @issuer ||= decoded_assertion.iss
end

#jwt_decode(with_validation: true) ⇒ Object (private)



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'app/services/sign_in/assertion_validator.rb', line 145

def jwt_decode(with_validation: true)
  assertion_public_keys = with_validation ? .assertion_public_keys : nil
  decoded_jwt = JWT.decode(
    assertion,
    assertion_public_keys,
    with_validation,
    { verify_expiration: with_validation, algorithm: Constants::Auth::ASSERTION_ENCODE_ALGORITHM }
  )&.first
  OpenStruct.new(decoded_jwt)
rescue JWT::VerificationError
  raise Errors::AssertionSignatureMismatchError.new message: 'Assertion body does not match signature'
rescue JWT::ExpiredSignature
  raise Errors::AssertionExpiredError.new message: 'Assertion has expired'
rescue JWT::DecodeError
  raise Errors::AssertionMalformedJWTError.new message: 'Assertion is malformed'
end

#localhost_hostnameObject (private)



162
163
164
165
166
# File 'app/services/sign_in/assertion_validator.rb', line 162

def localhost_hostname
  port = URI.parse("http://#{Settings.hostname}").port

  "http://localhost:#{port}"
end

#performObject



11
12
13
14
15
16
17
18
19
20
21
# File 'app/services/sign_in/assertion_validator.rb', line 11

def perform
  
  validate_issuer
  validate_audience
  validate_scopes
  validate_user_attributes
  validate_subject
  validate_issued_at_time
  validate_expiration
  create_new_access_token
end

#scopesObject (private)



109
110
111
# File 'app/services/sign_in/assertion_validator.rb', line 109

def scopes
  @scopes ||= Array(decoded_assertion.scopes)
end

#service_account_configObject (private)



141
142
143
# File 'app/services/sign_in/assertion_validator.rb', line 141

def 
  @service_account_config ||= ServiceAccountConfig.find_by(service_account_id:)
end

#service_account_idObject (private)



105
106
107
# File 'app/services/sign_in/assertion_validator.rb', line 105

def 
  @service_account_id ||= decoded_assertion_without_validation.
end

#token_routeObject (private)



87
88
89
# File 'app/services/sign_in/assertion_validator.rb', line 87

def token_route
  "#{hostname}#{Constants::Auth::TOKEN_ROUTE_PATH}"
end

#user_attributesObject (private)



113
114
115
# File 'app/services/sign_in/assertion_validator.rb', line 113

def user_attributes
  @user_attributes = decoded_assertion.user_attributes || {}
end

#user_identifierObject (private)



117
118
119
# File 'app/services/sign_in/assertion_validator.rb', line 117

def user_identifier
  @user_identifier ||= decoded_assertion.sub
end

#validate_audienceObject (private)



37
38
39
40
41
# File 'app/services/sign_in/assertion_validator.rb', line 37

def validate_audience
  unless assertion_audience.include?(token_route)
    raise Errors::ServiceAccountAssertionAttributesError.new message: 'Assertion audience is not valid'
  end
end

#validate_expirationObject (private)



67
68
69
70
71
# File 'app/services/sign_in/assertion_validator.rb', line 67

def validate_expiration
  if expiration.blank?
    raise Errors::ServiceAccountAssertionAttributesError.new message: 'Assertion expiration timestamp is not valid'
  end
end

#validate_issued_at_timeObject (private)



61
62
63
64
65
# File 'app/services/sign_in/assertion_validator.rb', line 61

def validate_issued_at_time
  if issued_at_time.blank? || issued_at_time > Time.now.to_i
    raise Errors::ServiceAccountAssertionAttributesError.new message: 'Assertion issuance timestamp is not valid'
  end
end

#validate_issuerObject (private)



31
32
33
34
35
# File 'app/services/sign_in/assertion_validator.rb', line 31

def validate_issuer
  if issuer != audience
    raise Errors::ServiceAccountAssertionAttributesError.new message: 'Assertion issuer is not valid'
  end
end

#validate_scopesObject (private)



43
44
45
46
47
# File 'app/services/sign_in/assertion_validator.rb', line 43

def validate_scopes
  unless decoded_assertion_scopes_are_defined_in_config?
    raise Errors::ServiceAccountAssertionAttributesError.new message: 'Assertion scopes are not valid'
  end
end

#validate_service_account_configObject (private)



25
26
27
28
29
# File 'app/services/sign_in/assertion_validator.rb', line 25

def 
  if .blank?
    raise Errors::ServiceAccountConfigNotFound.new message: 'Service account config not found'
  end
end

#validate_subjectObject (private)



55
56
57
58
59
# File 'app/services/sign_in/assertion_validator.rb', line 55

def validate_subject
  if user_identifier.blank?
    raise Errors::ServiceAccountAssertionAttributesError.new message: 'Assertion subject is not valid'
  end
end

#validate_user_attributesObject (private)



49
50
51
52
53
# File 'app/services/sign_in/assertion_validator.rb', line 49

def validate_user_attributes
  if (user_attributes.keys.map(&:to_s) - .access_token_user_attributes).any?
    raise Errors::ServiceAccountAssertionAttributesError.new message: 'Assertion user attributes are not valid'
  end
end