-
Notifications
You must be signed in to change notification settings - Fork 141
/
Copy pathauthentication.rb
182 lines (158 loc) · 6.92 KB
/
authentication.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
module Api
class BaseController
module Authentication
class AuthenticationError < StandardError; end
SYSTEM_TOKEN_TTL = 30.seconds
def auth_mechanism
if request.headers[HttpHeaders::MIQ_TOKEN]
:system
elsif request.headers[HttpHeaders::AUTH_TOKEN]
:token
elsif request.headers["HTTP_AUTHORIZATION"].try(:match, /^Bearer (.*)/)
:jwt
elsif request.headers["HTTP_AUTHORIZATION"]
# For AJAX requests the basic auth type should be distinguished
request.headers['X-REQUESTED-WITH'] == 'XMLHttpRequest' ? :basic_async : :basic
elsif request.cookies['_vmdb_session'] || request.x_csrf_token
# Even if the session cookie is not set, we want to consider a request
# as a UI authentication request. (Previously that would force a login popup.)
:ui_session
else
# no attempt at authentication, usually falls back to :basic
nil
end
end
def authenticate_user
case auth_mechanism
when :system
authenticate_with_system_token(request.headers[HttpHeaders::MIQ_TOKEN])
when :token
authenticate_with_user_token(request.headers[HttpHeaders::AUTH_TOKEN])
when :ui_session
raise AuthenticationError unless valid_ui_session?
auth_user(session[:userid])
when :jwt
authenticate_with_jwt
when :basic, :basic_async, nil
success = authenticate_with_http_basic { |u, p| basic_authentication(u, p) }
raise AuthenticationError unless success
end
log_api_auth
end
#
# REST APIs Authenticator and Redirector
#
def require_api_user_or_token
authenticate_user
rescue AuthenticationError => e
api_log_error("AuthenticationError: #{e.message}")
response.headers["Content-Type"] = "application/json"
error_message = ErrorSerializer.new(:unauthorized, e).serialize(true).to_json
render :status => 401, :json => error_message
log_api_response
end
#
# Attempt auth if desired, but flow through on failure
#
def optional_api_user_or_token
authenticate_user
rescue AuthenticationError => e
api_log_warn("AuthenticationError: #{e.message} (on #{request.method} that doesn't require it... ignoring)")
end
def user_settings
{
:locale => I18n.locale.to_s.sub('-', '_'),
:asynchronous_notifications => ::Settings.server.asynchronous_notifications,
}.merge(User.current_user.settings)
end
def authorize_user_group(user_obj)
group_name = request.headers[HttpHeaders::MIQ_GROUP]
if group_name.present?
group_name = CGI.unescape(group_name)
group_obj = user_obj.miq_groups.find_by(:description => group_name)
raise AuthenticationError, "Invalid Authorization Group #{group_name} specified" if group_obj.nil?
user_obj.current_group_by_description = group_name
end
end
def validate_user_identity(user_obj)
missing_feature = User.missing_user_features(user_obj)
if missing_feature
raise AuthenticationError, "Invalid User #{user_obj.userid} specified, User's #{missing_feature} is missing"
end
end
private
def clear_cached_current_user
User.current_user = nil
end
def api_token_mgr
Environment.user_token_service.token_mgr('api')
end
def auth_user(userid)
auth_user_obj = User.lookup_by_identity(userid)
authorize_user_group(auth_user_obj)
validate_user_identity(auth_user_obj)
User.current_user = auth_user_obj
end
def authenticate_with_user_token(auth_token)
if !api_token_mgr.token_valid?(auth_token)
raise AuthenticationError, "Invalid Authentication Token #{auth_token} specified"
else
userid = api_token_mgr.token_get_info(auth_token, :userid)
raise AuthenticationError, "Invalid Authentication Token #{auth_token} specified" unless userid
unless request.headers['X-Auth-Skip-Token-Renewal'] == 'true'
api_token_mgr.reset_token(auth_token)
end
auth_user(userid)
end
rescue TokenManager::MissingTokenError
raise AuthenticationError, "Missing Authentication Token (#{HttpHeaders::AUTH_TOKEN})"
rescue TokenManager::EmptyTokenError
raise AuthenticationError, "Empty Authentication Token (#{HttpHeaders::AUTH_TOKEN})"
end
def authenticate_with_system_token(x_miq_token)
@miq_token_hash = YAML.load(ManageIQ::Password.decrypt(x_miq_token))
validate_system_token_server(@miq_token_hash[:server_guid])
validate_system_token_timestamp(@miq_token_hash[:timestamp])
if @miq_token_hash[:user_metadata].present?
User.authorize_user_with_system_token(@miq_token_hash[:userid], @miq_token_hash[:user_metadata])
else
User.authorize_user(@miq_token_hash[:userid])
end
auth_user(@miq_token_hash[:userid])
rescue => err
api_log_error("Authentication Failed with System Token\nX-MIQ-Token: #{x_miq_token}\nError: #{err}")
raise AuthenticationError, "Invalid System Authentication Token specified"
end
def validate_system_token_server(server_guid)
raise "Missing server_guid" if server_guid.blank?
raise "Invalid server_guid #{server_guid} specified" unless MiqServer.where(:guid => server_guid).exists?
end
def validate_system_token_timestamp(timestamp)
raise "Missing timestamp" if timestamp.blank?
raise "Invalid timestamp #{timestamp} specified" if SYSTEM_TOKEN_TTL.ago.utc > timestamp
end
def valid_ui_session?
[
valid_authenticity_token?(session, request.x_csrf_token) || request.method == 'GET', # CSRF token be set and valid (or GET)
session[:userid].present?, # session has a userid stored
request.origin.nil? || request.origin == request.base_url # origin header if set matches base_url
].all?
end
def authenticate_with_jwt
timeout = ::Settings.api.authentication_timeout.to_i_with_method
user_header = request.headers["X-REMOTE-USER"]&.force_encoding("UTF-8")
user = User.authenticate(user_header, "", request, :require_user => true, :timeout => timeout)
auth_user(user.userid)
rescue => e
raise AuthenticationError, "Failed to Authenticate with JWT - error #{e}"
end
def basic_authentication(username, password)
timeout = ::Settings.api.authentication_timeout.to_i_with_method
user = User.authenticate(username, password, request, :require_user => true, :timeout => timeout)
auth_user(user.userid)
rescue MiqException::MiqEVMLoginError => e
raise AuthenticationError, e.message
end
end
end
end