@@ -56,9 +56,26 @@ type OAuth2Configs struct {
56
56
// ExtractAPIKey requires authentication using a valid API key.
57
57
// It handles extending an API key if it comes close to expiry,
58
58
// updating the last used time in the database.
59
- func ExtractAPIKey (db database.Store , oauth * OAuth2Configs ) func (http.Handler ) http.Handler {
59
+ // nolint:revive
60
+ func ExtractAPIKey (db database.Store , oauth * OAuth2Configs , redirectToLogin bool ) func (http.Handler ) http.Handler {
60
61
return func (next http.Handler ) http.Handler {
61
62
return http .HandlerFunc (func (rw http.ResponseWriter , r * http.Request ) {
63
+ // Write wraps writing a response to redirect if the handler
64
+ // specified it should. This redirect is used for user-facing
65
+ // pages like workspace applications.
66
+ write := func (code int , response httpapi.Response ) {
67
+ if redirectToLogin {
68
+ q := r .URL .Query ()
69
+ q .Add ("message" , response .Message )
70
+ q .Add ("redirect" , r .URL .Path + "?" + r .URL .RawQuery )
71
+ r .URL .RawQuery = q .Encode ()
72
+ r .URL .Path = "/login"
73
+ http .Redirect (rw , r , r .URL .String (), http .StatusTemporaryRedirect )
74
+ return
75
+ }
76
+ httpapi .Write (rw , code , response )
77
+ }
78
+
62
79
var cookieValue string
63
80
cookie , err := r .Cookie (SessionTokenKey )
64
81
if err != nil {
@@ -67,15 +84,15 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
67
84
cookieValue = cookie .Value
68
85
}
69
86
if cookieValue == "" {
70
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
87
+ write ( http .StatusUnauthorized , httpapi.Response {
71
88
Message : fmt .Sprintf ("Cookie %q or query parameter must be provided." , SessionTokenKey ),
72
89
})
73
90
return
74
91
}
75
92
parts := strings .Split (cookieValue , "-" )
76
93
// APIKeys are formatted: ID-SECRET
77
94
if len (parts ) != 2 {
78
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
95
+ write ( http .StatusUnauthorized , httpapi.Response {
79
96
Message : fmt .Sprintf ("Invalid %q cookie API key format." , SessionTokenKey ),
80
97
})
81
98
return
@@ -84,26 +101,26 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
84
101
keySecret := parts [1 ]
85
102
// Ensuring key lengths are valid.
86
103
if len (keyID ) != 10 {
87
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
104
+ write ( http .StatusUnauthorized , httpapi.Response {
88
105
Message : fmt .Sprintf ("Invalid %q cookie API key id." , SessionTokenKey ),
89
106
})
90
107
return
91
108
}
92
109
if len (keySecret ) != 22 {
93
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
110
+ write ( http .StatusUnauthorized , httpapi.Response {
94
111
Message : fmt .Sprintf ("Invalid %q cookie API key secret." , SessionTokenKey ),
95
112
})
96
113
return
97
114
}
98
115
key , err := db .GetAPIKeyByID (r .Context (), keyID )
99
116
if err != nil {
100
117
if errors .Is (err , sql .ErrNoRows ) {
101
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
118
+ write ( http .StatusUnauthorized , httpapi.Response {
102
119
Message : "API key is invalid." ,
103
120
})
104
121
return
105
122
}
106
- httpapi . Write ( rw , http .StatusInternalServerError , httpapi.Response {
123
+ write ( http .StatusInternalServerError , httpapi.Response {
107
124
Message : "Internal error fetching API key by id." ,
108
125
Detail : err .Error (),
109
126
})
@@ -113,7 +130,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
113
130
114
131
// Checking to see if the secret is valid.
115
132
if subtle .ConstantTimeCompare (key .HashedSecret , hashed [:]) != 1 {
116
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
133
+ write ( http .StatusUnauthorized , httpapi.Response {
117
134
Message : "API key secret is invalid." ,
118
135
})
119
136
return
@@ -130,7 +147,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
130
147
case database .LoginTypeGithub :
131
148
oauthConfig = oauth .Github
132
149
default :
133
- httpapi . Write ( rw , http .StatusInternalServerError , httpapi.Response {
150
+ write ( http .StatusInternalServerError , httpapi.Response {
134
151
Message : fmt .Sprintf ("Unexpected authentication type %q." , key .LoginType ),
135
152
})
136
153
return
@@ -142,7 +159,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
142
159
Expiry : key .OAuthExpiry ,
143
160
}).Token ()
144
161
if err != nil {
145
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
162
+ write ( http .StatusUnauthorized , httpapi.Response {
146
163
Message : "Could not refresh expired Oauth token." ,
147
164
Detail : err .Error (),
148
165
})
@@ -158,7 +175,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
158
175
159
176
// Checking if the key is expired.
160
177
if key .ExpiresAt .Before (now ) {
161
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
178
+ write ( http .StatusUnauthorized , httpapi.Response {
162
179
Message : fmt .Sprintf ("API key expired at %q." , key .ExpiresAt .String ()),
163
180
})
164
181
return
@@ -200,7 +217,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
200
217
OAuthExpiry : key .OAuthExpiry ,
201
218
})
202
219
if err != nil {
203
- httpapi . Write ( rw , http .StatusInternalServerError , httpapi.Response {
220
+ write ( http .StatusInternalServerError , httpapi.Response {
204
221
Message : fmt .Sprintf ("API key couldn't update: %s." , err .Error ()),
205
222
})
206
223
return
@@ -212,15 +229,15 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
212
229
// is to block 'suspended' users from accessing the platform.
213
230
roles , err := db .GetAuthorizationUserRoles (r .Context (), key .UserID )
214
231
if err != nil {
215
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
232
+ write ( http .StatusUnauthorized , httpapi.Response {
216
233
Message : "Internal error fetching user's roles." ,
217
234
Detail : err .Error (),
218
235
})
219
236
return
220
237
}
221
238
222
239
if roles .Status != database .UserStatusActive {
223
- httpapi . Write ( rw , http .StatusUnauthorized , httpapi.Response {
240
+ write ( http .StatusUnauthorized , httpapi.Response {
224
241
Message : fmt .Sprintf ("User is not active (status = %q). Contact an admin to reactivate your account." , roles .Status ),
225
242
})
226
243
return
0 commit comments