|
3 | 3 | require 'faraday_middleware' |
4 | 4 | require 'securerandom' |
5 | 5 | require 'travis/api/app' |
6 | | -require 'travis/github/education' |
7 | | -require 'travis/github/oauth' |
8 | 6 | require 'travis/remote_vcs/user' |
9 | 7 | require 'travis/remote_vcs/response_error' |
10 | 8 | require 'uri' |
@@ -92,22 +90,20 @@ class Authorization < Endpoint |
92 | 90 | end |
93 | 91 |
|
94 | 92 | # For new provider method |
95 | | - # renew_access_token(token: params[:github_token], app_id: 1, provider: :github) |
96 | | - { 'access_token' => github_to_travis(params[:github_token], app_id: 1, drop_token: true) } |
| 93 | + renew_access_token(token: params[:github_token], app_id: 1, provider: :github) |
97 | 94 | end |
98 | 95 |
|
99 | | - # Endpoint for making sure user authorized Travis CI to access GitHub. |
| 96 | + # Endpoint for making sure user authorized Travis CI to access VCS provider. |
100 | 97 | # There are no restrictions on where to redirect to after handshake. |
101 | 98 | # However, no information whatsoever is being sent with the redirect. |
102 | 99 | # |
103 | 100 | # Parameters: |
104 | 101 | # |
105 | 102 | # * **redirect_uri**: URI to redirect to after handshake. |
106 | 103 | get '/handshake/?:provider?' do |
107 | | - method = org? ? :handshake : :vcs_handshake |
108 | 104 | params[:provider] ||= 'github' |
109 | | - send(method) do |user, token, redirect_uri| |
110 | | - if target_ok? redirect_uri |
| 105 | + vcs_handshake do |user, token, redirect_uri| |
| 106 | + if target_ok?(redirect_uri) |
111 | 107 | content_type :html |
112 | 108 | data = { user: user, token: token, uri: redirect_uri } |
113 | 109 | erb(:post_payload, locals: data) |
@@ -151,42 +147,6 @@ def log_with_request_id(line) |
151 | 147 | Travis.logger.info "#{line} <request_id=#{request_id}>" |
152 | 148 | end |
153 | 149 |
|
154 | | - def handshake |
155 | | - config = Travis.config.oauth2.to_h |
156 | | - endpoint = Addressable::URI.parse(config[:authorization_server]) |
157 | | - values = { |
158 | | - client_id: config[:client_id], |
159 | | - scope: config[:scope], |
160 | | - redirect_uri: oauth_endpoint |
161 | | - } |
162 | | - |
163 | | - log_with_request_id("[handshake] Starting handshake") |
164 | | - |
165 | | - if params[:code] |
166 | | - unless state_ok?(params[:state]) |
167 | | - log_with_request_id("[handshake] Handshake failed (state mismatch)") |
168 | | - handle_invalid_response |
169 | | - return |
170 | | - end |
171 | | - |
172 | | - endpoint.path = config[:access_token_path] |
173 | | - values[:state] = params[:state] |
174 | | - values[:code] = params[:code] |
175 | | - values[:client_secret] = config[:client_secret] |
176 | | - github_token = get_token(endpoint.to_s, values) |
177 | | - user = user_for_github_token(github_token) |
178 | | - token = generate_token(user: user, app_id: 0) |
179 | | - payload = params[:state].split(":::", 2)[1] |
180 | | - update_first_login(user) |
181 | | - yield serialize_user(user), token, payload |
182 | | - else |
183 | | - values[:state] = create_state |
184 | | - endpoint.path = config[:authorize_path] |
185 | | - endpoint.query_values = values |
186 | | - redirect to(endpoint.to_s) |
187 | | - end |
188 | | - end |
189 | | - |
190 | 150 | # VCS HANDSHAKE START |
191 | 151 |
|
192 | 152 | def remote_vcs_user |
@@ -274,171 +234,11 @@ def handle_invalid_response |
274 | 234 | redirect to("https://#{Travis.config.host}/") |
275 | 235 | end |
276 | 236 |
|
277 | | - def create_state |
278 | | - state = SecureRandom.urlsafe_base64(16) |
279 | | - redis.sadd('github:states', state) |
280 | | - redis.expire('github:states', 1800) |
281 | | - payload = params[:origin] || params[:redirect_uri] |
282 | | - state << ":::" << payload if payload |
283 | | - response.set_cookie(cookie_name, state) |
284 | | - state |
285 | | - end |
286 | | - |
287 | 237 | def state_ok?(state, provider = :github) |
288 | 238 | cookie_state = request.cookies[cookie_name(provider)] |
289 | 239 | state == cookie_state and redis.srem('github:states', state.to_s.split(":::", 1)) |
290 | 240 | end |
291 | 241 |
|
292 | | - def github_to_travis(token, options = {}) |
293 | | - drop_token = options.delete(:drop_token) |
294 | | - generate_token options.merge(user: user_for_github_token(token, drop_token)) |
295 | | - end |
296 | | - |
297 | | - class UserManager < Struct.new(:data, :token, :drop_token) |
298 | | - include User::Renaming |
299 | | - |
300 | | - attr_accessor :user |
301 | | - |
302 | | - def initialize(*) |
303 | | - super |
304 | | - |
305 | | - @user = ::User.find_by_github_id(data['id']) |
306 | | - end |
307 | | - |
308 | | - def info(attributes = {}) |
309 | | - info = data.to_hash.slice('name', 'login', 'gravatar_id') |
310 | | - info.merge! attributes.stringify_keys |
311 | | - if Travis::Features.feature_active?(:education_data_sync) || |
312 | | - (user && Travis::Features.owner_active?(:education_data_sync, user)) |
313 | | - info['education'] = education |
314 | | - end |
315 | | - info['github_id'] ||= data['id'] |
316 | | - info['vcs_id'] ||= data['id'] |
317 | | - info |
318 | | - end |
319 | | - |
320 | | - def user_exists? |
321 | | - user |
322 | | - end |
323 | | - |
324 | | - def education |
325 | | - Travis::Github::Education.new(token.to_s).student? |
326 | | - end |
327 | | - |
328 | | - def fetch |
329 | | - retried ||= false |
330 | | - info = drop_token ? self.info : self.info(github_oauth_token: token) |
331 | | - |
332 | | - ActiveRecord::Base.transaction do |
333 | | - if user |
334 | | - ensure_token_is_available |
335 | | - rename_repos_owner(user.login, info['login']) |
336 | | - user.update_attributes info |
337 | | - else |
338 | | - self.user = ::User.create! info |
339 | | - end |
340 | | - |
341 | | - Travis::Github::Oauth.update_scopes(user) # unless Travis.env == 'test' |
342 | | - |
343 | | - nullify_logins(user.github_id, user.login) |
344 | | - end |
345 | | - |
346 | | - user |
347 | | - rescue ActiveRecord::RecordNotUnique |
348 | | - unless retried |
349 | | - retried = true |
350 | | - retry |
351 | | - end |
352 | | - end |
353 | | - |
354 | | - def ensure_token_is_available |
355 | | - unless user.tokens.first |
356 | | - user.create_a_token |
357 | | - end |
358 | | - end |
359 | | - end |
360 | | - |
361 | | - def user_for_github_token(token, drop_token = false) |
362 | | - data = GH.with(token: token.to_s, client_id: nil) { GH['user'] } |
363 | | - scopes = parse_scopes data.headers['x-oauth-scopes'] |
364 | | - manager = UserManager.new(data, token, drop_token) |
365 | | - |
366 | | - unless acceptable?(scopes, drop_token) |
367 | | - # TODO: we should probably only redirect if this is a web |
368 | | - # oauth request, are there any other possibilities to |
369 | | - # consider? |
370 | | - url = Travis.config.oauth2.insufficient_access_redirect_url |
371 | | - url += "#existing-user" if manager.user_exists? |
372 | | - redirect to(url) |
373 | | - end |
374 | | - |
375 | | - user = manager.fetch |
376 | | - if user.nil? |
377 | | - log_with_request_id("[handshake] Fetching user failed") |
378 | | - halt 403, 'not a Travis user' |
379 | | - end |
380 | | - |
381 | | - Travis.run_service(:sync_user, user) |
382 | | - |
383 | | - user |
384 | | - rescue GH::Error |
385 | | - # not a valid token actually, but we don't want to expose that info |
386 | | - halt 403, 'not a Travis user' |
387 | | - end |
388 | | - |
389 | | - def get_token(endpoint, values) |
390 | | - # Get base URL for when we setup Faraday since otherwise it'll ignore no_proxy |
391 | | - url = URI.parse(endpoint) |
392 | | - base_url = "#{url.scheme}://#{url.host}" |
393 | | - http_options = {url: base_url, ssl: Travis.config.ssl.to_h.merge(Travis.config.github.ssl || {}).compact} |
394 | | - |
395 | | - conn = Faraday.new(http_options) do |conn| |
396 | | - conn.request :json |
397 | | - conn.use :instrumentation |
398 | | - conn.use OpenCensus::Trace::Integrations::FaradayMiddleware if Travis::Api::App::Middleware::OpenCensus.enabled? |
399 | | - conn.adapter :net_http_persistent |
400 | | - end |
401 | | - response = conn.post(endpoint, values) |
402 | | - |
403 | | - parameters = Addressable::URI.form_unencode(response.body) |
404 | | - token_info = parameters.assoc("access_token") |
405 | | - |
406 | | - unless token_info |
407 | | - log_with_request_id("[handshake] Could not fetch token, github's response: status=#{response.status}, body=#{parameters.inspect} headers=#{response.headers.inspect}") |
408 | | - halt 401, 'could not resolve github token' |
409 | | - end |
410 | | - token_info.last |
411 | | - end |
412 | | - |
413 | | - def parse_scopes(data) |
414 | | - data.gsub(/\s/,'').split(',') if data |
415 | | - end |
416 | | - |
417 | | - def generate_token(options) |
418 | | - AccessToken.create(options).token |
419 | | - end |
420 | | - |
421 | | - def acceptable?(scopes, lossy = false) |
422 | | - Travis::Github::Oauth.wanted_scopes.all? do |scope| |
423 | | - acceptable_scopes_for(scope, lossy).any? { |s| scopes.include? s } |
424 | | - end |
425 | | - end |
426 | | - |
427 | | - def acceptable_scopes_for(scope, lossy = false) |
428 | | - scopes = case scope = scope.to_s |
429 | | - when /^(.+):/ then [$1, scope] |
430 | | - when 'public_repo' then [scope, 'repo'] |
431 | | - else [scope] |
432 | | - end |
433 | | - |
434 | | - if lossy |
435 | | - scopes << 'repo' |
436 | | - scopes << 'public_repo' if lossy and scope != 'repo' |
437 | | - end |
438 | | - |
439 | | - scopes |
440 | | - end |
441 | | - |
442 | 242 | def post_message(payload) |
443 | 243 | content_type :html |
444 | 244 | erb(:post_message, locals: payload) |
|
0 commit comments