Class: AuthenticationStrategies::VomsStrategy

Inherits:
Warden::Strategies::Base
  • Object
show all
Defined in:
lib/authentication_strategies/voms_strategy.rb

Constant Summary collapse

VOMS_RANGE =
(0..100)
GRST_CRED_REGEXP =
/^(.+)\s(\d+)\s(\d+)\s(\d)\s(.+)$/
GRST_VOMS_REGEXP =
/^\/(.+)\/Role=(.+)\/Capability=(.+)$/
ROBOT_SUBPROXY_REGEXP =
/^(?<issuer_base>\/.+)\/CN=Robot(:|\/|\s\-\s)(?<robot_name>[^\/]+)\/CN=eToken:(?<subuser_name>[^\/]+)(\/CN=\d+)+$/

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.allowed_access?(vo_name) ⇒ Boolean

Returns:

  • (Boolean)

135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/authentication_strategies/voms_strategy.rb', line 135

def allowed_access?(vo_name)
  return false if vo_name.blank?

  case OPTIONS.access_policy
  when 'blacklist'
    !blacklisted_vo?(vo_name)
  when 'whitelist'
    whitelisted_vo?(vo_name)
  else
    raise Errors::ConfigurationParsingError,
          "Unsupported VOMS access policy #{OPTIONS.access_policy.inspect}!"
  end
end

.blacklisted_vo?(vo_name) ⇒ Boolean

Returns:

  • (Boolean)

149
150
151
152
# File 'lib/authentication_strategies/voms_strategy.rb', line 149

def blacklisted_vo?(vo_name)
  blacklist = AuthenticationStrategies::Helpers::YamlHelper.read_yaml(OPTIONS.blacklist) || []
  blacklist.include?(vo_name)
end

.extract_robot_etoken(matched_robot, auth_request) ⇒ Object


73
74
75
76
77
78
79
80
81
82
83
# File 'lib/authentication_strategies/voms_strategy.rb', line 73

def extract_robot_etoken(matched_robot, auth_request)
  Rails.logger.debug "[AuthN] [#{self}] Matched robot #{matched_robot[:robot_name].inspect} " \
                     "and sub-user #{matched_robot[:subuser_name].inspect}"
  w_etoken = GRST_CRED_REGEXP.match(auth_request.env["GRST_CRED_1"])
  w_etoken = w_etoken.to_a.drop 1

  Rails.logger.debug "[AuthN] [#{self}] Looking at GRST_CRED_1 => " \
                     "#{auth_request.env["GRST_CRED_1"].inspect} and its last " \
                     "element => #{w_etoken[4].inspect}"
  w_etoken[4]
end

.handle_robots?Boolean

Returns:

  • (Boolean)

169
170
171
# File 'lib/authentication_strategies/voms_strategy.rb', line 169

def handle_robots?
  OPTIONS.robot_subproxy_identity
end

.mapped_vo_name(vo_name) ⇒ Object


159
160
161
162
163
164
165
166
167
# File 'lib/authentication_strategies/voms_strategy.rb', line 159

def mapped_vo_name(vo_name)
  return vo_name unless OPTIONS.vo_mapping

  map = AuthenticationStrategies::Helpers::YamlHelper.read_yaml(OPTIONS.vo_mapfile) || {}
  new_vo_name = map[vo_name] || vo_name

  Rails.logger.debug "[AuthN] [#{self}] VO name mapped #{vo_name.inspect} -> #{new_vo_name.inspect}"
  new_vo_name
end

.voms_extension_attrs(auth_request) ⇒ Object


101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/authentication_strategies/voms_strategy.rb', line 101

def voms_extension_attrs(auth_request)
  attributes = []

  VOMS_RANGE.each do |index|
    break if auth_request.env["GRST_CRED_#{index}"].blank?

    if auth_request.env["GRST_CRED_#{index}"].start_with?('VOMS')
      # Parse the extension and drop useless first element of MatchData
      voms_ext = GRST_CRED_REGEXP.match(auth_request.env["GRST_CRED_#{index}"])
      voms_ext = voms_ext.to_a.drop 1
      break if voms_ext.empty?

      # Parse group, role and capability from the VOMS extension
      voms_ary = GRST_VOMS_REGEXP.match(voms_ext[4])
      voms_ary = voms_ary.to_a.drop 1
      break if voms_ary.empty?

      voms_attrs = Hashie::Mash.new
      voms_attrs.vo = mapped_vo_name(voms_ary[0])
      voms_attrs.role = voms_ary[1]
      voms_attrs.capability = voms_ary[2]

      if allowed_access?(voms_attrs.vo)
        attributes << voms_attrs
      else
        Rails.logger.warn "[AuthN] [#{self}] VO #{voms_attrs.vo.inspect} is NOT allowed!"
      end
    end
  end

  Rails.logger.debug "[AuthN] [#{self}] VOMS attrs: #{attributes.inspect}"
  attributes
end

.voms_extensions?(auth_request) ⇒ Boolean

Returns:

  • (Boolean)

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/authentication_strategies/voms_strategy.rb', line 85

def voms_extensions?(auth_request)
  voms_ext = false

  VOMS_RANGE.each do |index|
    Rails.logger.debug "[AuthN] [#{self}] GRST_CRED_#{index}: " + auth_request.env["GRST_CRED_#{index}"].inspect
    break if auth_request.env["GRST_CRED_#{index}"].blank?

    if auth_request.env["GRST_CRED_#{index}"].start_with?('VOMS')
      voms_ext = true
      break
    end
  end

  voms_ext
end

.whitelisted_vo?(vo_name) ⇒ Boolean

Returns:

  • (Boolean)

154
155
156
157
# File 'lib/authentication_strategies/voms_strategy.rb', line 154

def whitelisted_vo?(vo_name)
  whitelist = AuthenticationStrategies::Helpers::YamlHelper.read_yaml(OPTIONS.whitelist) || []
  whitelist.include?(vo_name)
end

Instance Method Details

#auth_requestObject


8
9
10
# File 'lib/authentication_strategies/voms_strategy.rb', line 8

def auth_request
  @auth_request ||= ::ActionDispatch::Request.new(env)
end

#authenticate!Object

See Also:


29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/authentication_strategies/voms_strategy.rb', line 29

def authenticate!
  Rails.logger.debug "[AuthN] [#{self.class}] Authenticating #{auth_request.env['GRST_CRED_0'].inspect}"

  # Get user's DN
  proxy_cert_subject = (GRST_CRED_REGEXP.match(auth_request.env['GRST_CRED_0']) || [])[5]
  if proxy_cert_subject.blank?
    fail! 'Could not extract user\'s DN from credentials!'
    return
  end

  # Get VOMS extension attributes
  voms_cert_attrs = self.class.voms_extension_attrs(auth_request)
  if voms_cert_attrs.empty?
    fail! 'Could not extract VOMS attributes from user\'s credentials!'
    return
  end

  user = Hashie::Mash.new
  user.auth!.type = 'voms'
  user.auth!.credentials!.client_cert_dn = proxy_cert_subject
  user.auth!.credentials!.client_cert_voms_attrs = voms_cert_attrs
  user.auth!.credentials!.client_cert = auth_request.env['SSL_CLIENT_CERT'] unless auth_request.env['SSL_CLIENT_CERT'].blank?
  user.auth!.credentials!.verification_status = auth_request.env['SSL_CLIENT_VERIFY']

  # Use sub-proxy DN as user identity if we are handling robots
  # and the DN in question matches our restrictions
  user.identity = if self.class.handle_robots? && (matched_robot = auth_request.env['SSL_CLIENT_S_DN'].match(ROBOT_SUBPROXY_REGEXP))
                    etoken = self.class.extract_robot_etoken(matched_robot, auth_request)
                    if etoken.blank?
                      fail! 'Couldn\'t extract the first proxy DN of a robot certificate!'
                      return
                    end

                    etoken
                  else
                    user.auth.credentials.client_cert_dn
                  end

  Rails.logger.debug "[AuthN] [#{self.class}] Authenticated #{user.to_hash.inspect}"
  success! user.deep_freeze
end

#store?Boolean

Returns:

  • (Boolean)

See Also:


13
14
15
# File 'lib/authentication_strategies/voms_strategy.rb', line 13

def store?
  false
end

#valid?Boolean

Returns:

  • (Boolean)

See Also:


18
19
20
21
22
23
24
25
26
# File 'lib/authentication_strategies/voms_strategy.rb', line 18

def valid?
  # TODO: verify that we are running inside Apache2
  Rails.logger.debug "[AuthN] [#{self.class}] Checking for applicability"
  Rails.logger.debug "[AuthN] [#{self.class}] SSL_CLIENT_S_DN: #{auth_request.env['SSL_CLIENT_S_DN'].inspect}"
  result = !auth_request.env['SSL_CLIENT_S_DN'].blank? && self.class.voms_extensions?(auth_request)

  Rails.logger.debug "[AuthN] [#{self.class}] Strategy is #{result ? '' : 'not '}applicable!"
  result
end