mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 11:35:03 +01:00 
			
		
		
		
	* initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
		
			
				
	
	
		
			133 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			133 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2014 The Go Authors. All rights reserved.
 | |
| // Use of this source code is governed by a BSD-style
 | |
| // license that can be found in the LICENSE file.
 | |
| 
 | |
| package oauth2
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"io"
 | |
| 	"net/http"
 | |
| 	"sync"
 | |
| )
 | |
| 
 | |
| // Transport is an http.RoundTripper that makes OAuth 2.0 HTTP requests,
 | |
| // wrapping a base RoundTripper and adding an Authorization header
 | |
| // with a token from the supplied Sources.
 | |
| //
 | |
| // Transport is a low-level mechanism. Most code will use the
 | |
| // higher-level Config.Client method instead.
 | |
| type Transport struct {
 | |
| 	// Source supplies the token to add to outgoing requests'
 | |
| 	// Authorization headers.
 | |
| 	Source TokenSource
 | |
| 
 | |
| 	// Base is the base RoundTripper used to make HTTP requests.
 | |
| 	// If nil, http.DefaultTransport is used.
 | |
| 	Base http.RoundTripper
 | |
| 
 | |
| 	mu     sync.Mutex                      // guards modReq
 | |
| 	modReq map[*http.Request]*http.Request // original -> modified
 | |
| }
 | |
| 
 | |
| // RoundTrip authorizes and authenticates the request with an
 | |
| // access token. If no token exists or token is expired,
 | |
| // tries to refresh/fetch a new token.
 | |
| func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) {
 | |
| 	if t.Source == nil {
 | |
| 		return nil, errors.New("oauth2: Transport's Source is nil")
 | |
| 	}
 | |
| 	token, err := t.Source.Token()
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	req2 := cloneRequest(req) // per RoundTripper contract
 | |
| 	token.SetAuthHeader(req2)
 | |
| 	t.setModReq(req, req2)
 | |
| 	res, err := t.base().RoundTrip(req2)
 | |
| 	if err != nil {
 | |
| 		t.setModReq(req, nil)
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	res.Body = &onEOFReader{
 | |
| 		rc: res.Body,
 | |
| 		fn: func() { t.setModReq(req, nil) },
 | |
| 	}
 | |
| 	return res, nil
 | |
| }
 | |
| 
 | |
| // CancelRequest cancels an in-flight request by closing its connection.
 | |
| func (t *Transport) CancelRequest(req *http.Request) {
 | |
| 	type canceler interface {
 | |
| 		CancelRequest(*http.Request)
 | |
| 	}
 | |
| 	if cr, ok := t.base().(canceler); ok {
 | |
| 		t.mu.Lock()
 | |
| 		modReq := t.modReq[req]
 | |
| 		delete(t.modReq, req)
 | |
| 		t.mu.Unlock()
 | |
| 		cr.CancelRequest(modReq)
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func (t *Transport) base() http.RoundTripper {
 | |
| 	if t.Base != nil {
 | |
| 		return t.Base
 | |
| 	}
 | |
| 	return http.DefaultTransport
 | |
| }
 | |
| 
 | |
| func (t *Transport) setModReq(orig, mod *http.Request) {
 | |
| 	t.mu.Lock()
 | |
| 	defer t.mu.Unlock()
 | |
| 	if t.modReq == nil {
 | |
| 		t.modReq = make(map[*http.Request]*http.Request)
 | |
| 	}
 | |
| 	if mod == nil {
 | |
| 		delete(t.modReq, orig)
 | |
| 	} else {
 | |
| 		t.modReq[orig] = mod
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // cloneRequest returns a clone of the provided *http.Request.
 | |
| // The clone is a shallow copy of the struct and its Header map.
 | |
| func cloneRequest(r *http.Request) *http.Request {
 | |
| 	// shallow copy of the struct
 | |
| 	r2 := new(http.Request)
 | |
| 	*r2 = *r
 | |
| 	// deep copy of the Header
 | |
| 	r2.Header = make(http.Header, len(r.Header))
 | |
| 	for k, s := range r.Header {
 | |
| 		r2.Header[k] = append([]string(nil), s...)
 | |
| 	}
 | |
| 	return r2
 | |
| }
 | |
| 
 | |
| type onEOFReader struct {
 | |
| 	rc io.ReadCloser
 | |
| 	fn func()
 | |
| }
 | |
| 
 | |
| func (r *onEOFReader) Read(p []byte) (n int, err error) {
 | |
| 	n, err = r.rc.Read(p)
 | |
| 	if err == io.EOF {
 | |
| 		r.runFunc()
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| func (r *onEOFReader) Close() error {
 | |
| 	err := r.rc.Close()
 | |
| 	r.runFunc()
 | |
| 	return err
 | |
| }
 | |
| 
 | |
| func (r *onEOFReader) runFunc() {
 | |
| 	if fn := r.fn; fn != nil {
 | |
| 		fn()
 | |
| 		r.fn = nil
 | |
| 	}
 | |
| }
 |