|
7 | 7 | package api |
8 | 8 |
|
9 | 9 | import ( |
10 | | - "context" |
11 | 10 | "fmt" |
| 11 | + "github.com/topfreegames/extensions/middleware" |
| 12 | + "github.com/topfreegames/maestro/api/auth" |
12 | 13 | "net/http" |
13 | 14 | "strings" |
14 | 15 |
|
15 | | - "github.com/topfreegames/extensions/middleware" |
16 | 16 | "github.com/topfreegames/maestro/errors" |
17 | | - "github.com/topfreegames/maestro/login" |
18 | 17 | ) |
19 | 18 |
|
20 | 19 | //AccessMiddleware guarantees that the user is logged |
21 | 20 | type AccessMiddleware struct { |
22 | | - App *App |
23 | | - next http.Handler |
24 | | - enabled bool |
| 21 | + App *App |
| 22 | + next http.Handler |
25 | 23 | } |
26 | 24 |
|
27 | 25 | // NewAccessMiddleware returns an access middleware |
28 | 26 | func NewAccessMiddleware(a *App) *AccessMiddleware { |
29 | | - enabled := a.Config.GetBool("oauth.enabled") |
30 | 27 | return &AccessMiddleware{ |
31 | | - App: a, |
32 | | - enabled: enabled, |
33 | | - } |
34 | | -} |
35 | | - |
36 | | -const emailKey = contextKey("emailKey") |
37 | | - |
38 | | -func emailFromContext(ctx context.Context) string { |
39 | | - payload := ctx.Value(emailKey) |
40 | | - if payload == nil { |
41 | | - return "" |
| 28 | + App: a, |
42 | 29 | } |
43 | | - return payload.(string) |
44 | | -} |
45 | | - |
46 | | -// NewContextWithEmail adds the email from oauth into context |
47 | | -func NewContextWithEmail(ctx context.Context, email string) context.Context { |
48 | | - c := context.WithValue(ctx, emailKey, email) |
49 | | - return c |
50 | 30 | } |
51 | 31 |
|
52 | | -func basicAuthWithXForwardedUserEmail( |
53 | | - basicAuthUser, basicAuthPass string, r *http.Request, |
54 | | -) (string, bool) { |
55 | | - if basicAuthUser == "" && basicAuthPass == "" { |
56 | | - return "", false |
57 | | - } |
58 | | - user, pass, ok := r.BasicAuth() |
59 | | - if !ok || user != basicAuthUser || pass != basicAuthPass { |
60 | | - return "", false |
61 | | - } |
62 | | - email := r.Header.Get("x-forwarded-user-email") |
63 | | - if email == "" { |
64 | | - return "", false |
65 | | - } |
66 | | - return email, true |
67 | | -} |
| 32 | +var emptyErr = fmt.Errorf("") |
68 | 33 |
|
69 | 34 | //ServeHTTP methods |
70 | 35 | func (m *AccessMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
71 | | - if !m.enabled { |
72 | | - m.next.ServeHTTP(w, r) |
73 | | - return |
74 | | - } |
| 36 | + ctx := r.Context() |
| 37 | + logger := middleware.GetLogger(ctx) |
| 38 | + if m.App.Config.GetBool("basicauth.enabled") { |
| 39 | + logger.Debug("checking basic auth") |
| 40 | + authPresent, authValid, email := auth.CheckBasicAuth(m.App.Config, r) |
| 41 | + |
| 42 | + // Basic Auth is valid, proceed to next handler |
| 43 | + if authValid { |
| 44 | + logger.Debug("basic auth ok") |
| 45 | + ctx = auth.NewContextWithEmail(ctx, email) |
| 46 | + ctx = auth.NewContextWithBasicAuthOK(ctx) |
| 47 | + m.next.ServeHTTP(w, r.WithContext(ctx)) |
| 48 | + return |
| 49 | + } |
75 | 50 |
|
76 | | - // checking basic auth in case of x-forwarded-user-email |
77 | | - basicAuthUser := m.App.Config.GetString("basicauth.username") |
78 | | - basicAuthPass := m.App.Config.GetString("basicauth.password") |
79 | | - if email, ok := basicAuthWithXForwardedUserEmail( |
80 | | - basicAuthUser, basicAuthPass, r, |
81 | | - ); ok { |
82 | | - ctx := NewContextWithEmail(r.Context(), email) |
83 | | - m.next.ServeHTTP(w, r.WithContext(ctx)) |
84 | | - return |
85 | | - } |
| 51 | + // Basic Auth is invalid, return unauthorized |
| 52 | + if authPresent { |
| 53 | + logger.Debug("basic auth invalid") |
| 54 | + m.App.HandleError(w, http.StatusUnauthorized, "authentication failed", fmt.Errorf("invalid basic auth")) |
| 55 | + return |
| 56 | + } |
86 | 57 |
|
87 | | - logger := middleware.GetLogger(r.Context()) |
88 | | - logger.Debug("Checking access token") |
| 58 | + logger.Debug("basic auth missing") |
| 59 | + // If basic auth is missing and `basicAuth.tryOauthIfUnset` is false return unauthorized |
| 60 | + if !m.App.Config.GetBool("basicAuth.tryOauthIfUnset") { |
| 61 | + logger.Debug("basic auth tryOauthIfUnset is false so unauthorized") |
| 62 | + m.App.HandleError(w, http.StatusUnauthorized, "authentication failed", fmt.Errorf("no basic auth sent")) |
| 63 | + return |
| 64 | + } |
| 65 | + } |
89 | 66 |
|
90 | | - accessToken := r.Header.Get("Authorization") |
91 | | - accessToken = strings.TrimPrefix(accessToken, "Bearer ") |
| 67 | + if m.App.Config.GetBool("william.enabled") { |
| 68 | + logger.Debug("william enabled, checking token") |
| 69 | + token := r.Header.Get("Authorization") |
| 70 | + if len(token) == 0 { |
| 71 | + logger.Debug("token empty") |
| 72 | + m.App.HandleError(w, http.StatusUnauthorized, "", errors.NewAccessError("missing access token", emptyErr)) |
| 73 | + return |
| 74 | + } |
92 | 75 |
|
93 | | - token, err := login.GetToken(accessToken, m.App.DBClient.WithContext(r.Context())) |
94 | | - if err != nil { |
95 | | - m.App.HandleError(w, http.StatusInternalServerError, "", err) |
96 | | - return |
97 | | - } |
98 | | - if token.RefreshToken == "" { |
99 | | - m.App.HandleError( |
100 | | - w, |
101 | | - http.StatusUnauthorized, |
102 | | - "", |
103 | | - errors.NewAccessError("access token was not found on db", fmt.Errorf("access token error")), |
104 | | - ) |
105 | | - return |
106 | | - } |
| 76 | + token = strings.TrimPrefix(token, "Bearer ") |
107 | 77 |
|
108 | | - msg, status, err := m.App.Login.Authenticate(token, m.App.DBClient.WithContext(r.Context())) |
109 | | - if err != nil { |
110 | | - logger.WithError(err).Error("error fetching googleapis") |
111 | | - m.App.HandleError(w, http.StatusInternalServerError, "Error fetching googleapis", err) |
112 | | - return |
113 | | - } |
| 78 | + if len(token) == 0 { |
| 79 | + logger.Debug("no bearer token") |
| 80 | + m.App.HandleError(w, http.StatusUnauthorized, "", errors.NewAccessError("Unauthorized access token", emptyErr)) |
| 81 | + return |
| 82 | + } |
114 | 83 |
|
115 | | - if status == http.StatusBadRequest { |
116 | | - logger.WithError(err).Error("error validating access token") |
117 | | - err := errors.NewAccessError("Unauthorized access token", fmt.Errorf(msg)) |
118 | | - m.App.HandleError(w, http.StatusUnauthorized, "Unauthorized access token", err) |
119 | | - return |
120 | | - } |
| 84 | + logger.Debug("token received") |
| 85 | + } else if m.App.Config.GetBool("oauth.enabled") { |
| 86 | + logger.Debug("oauth enabled, checking token") |
| 87 | + |
| 88 | + email, err := auth.CheckOauthToken(m.App.Login, m.App.DBClient.WithContext(ctx), logger, r, m.App.EmailDomains) |
| 89 | + if err != nil { |
| 90 | + if _, ok := err.(*errors.AccessError); ok { |
| 91 | + logger.Debug("authentication invalid") |
| 92 | + m.App.HandleError(w, http.StatusUnauthorized, "", err) |
| 93 | + } else { |
| 94 | + logger.Debug("authentication error") |
| 95 | + m.App.HandleError(w, http.StatusInternalServerError, "", err) |
| 96 | + } |
| 97 | + return |
| 98 | + } |
121 | 99 |
|
122 | | - if status != http.StatusOK { |
123 | | - logger.WithError(err).Error("invalid access token") |
124 | | - err := errors.NewAccessError("invalid access token", fmt.Errorf(msg)) |
125 | | - m.App.HandleError(w, status, "error validating access token", err) |
126 | | - return |
127 | | - } |
| 100 | + logger.Debug("token authenticated, putting email on context", email) |
128 | 101 |
|
129 | | - email := msg |
130 | | - if !verifyEmailDomain(email, m.App.EmailDomains) { |
131 | | - logger.WithError(err).Error("Invalid email") |
132 | | - err := errors.NewAccessError( |
133 | | - "authorization access error", |
134 | | - fmt.Errorf("the email on OAuth authorization is not from domain %s", m.App.EmailDomains), |
135 | | - ) |
136 | | - m.App.HandleError(w, http.StatusUnauthorized, "error validating access token", err) |
| 102 | + ctx = auth.NewContextWithEmail(r.Context(), email) |
| 103 | + m.next.ServeHTTP(w, r.WithContext(ctx)) |
137 | 104 | return |
138 | 105 | } |
139 | 106 |
|
140 | | - ctx := NewContextWithEmail(r.Context(), email) |
141 | | - |
142 | | - logger.Debug("Access token checked") |
143 | | - m.next.ServeHTTP(w, r.WithContext(ctx)) |
144 | | -} |
145 | | - |
146 | | -func verifyEmailDomain(email string, emailDomains []string) bool { |
147 | | - for _, domain := range emailDomains { |
148 | | - if strings.HasSuffix(email, domain) { |
149 | | - return true |
150 | | - } |
151 | | - } |
152 | | - return false |
| 107 | + m.next.ServeHTTP(w, r) |
153 | 108 | } |
154 | 109 |
|
155 | 110 | //SetNext handler |
|
0 commit comments