feat: add Error if the request can't be authorized

This commit is contained in:
Asbjørn Ulsberg 2023-04-30 23:44:41 +02:00
parent eac2daac64
commit a24db1f9f9
No known key found for this signature in database
GPG Key ID: 34616339C3D5E883
3 changed files with 52 additions and 0 deletions

View File

@ -7,6 +7,7 @@ package gin
import (
"crypto/subtle"
"encoding/base64"
"errors"
"net/http"
"strconv"
@ -26,6 +27,11 @@ type authPair struct {
type authPairs []authPair
var (
// ErrUnauthorized cannot authorize the request.
ErrUnauthorized = errors.New("unauthorized")
)
func (a authPairs) searchCredential(authValue string) (string, bool) {
if authValue == "" {
return "", false
@ -53,6 +59,7 @@ func BasicAuthForRealm(accounts Accounts, realm string) HandlerFunc {
user, found := pairs.searchCredential(c.requestHeader("Authorization"))
if !found {
// Credentials doesn't match, we return 401 and abort handlers chain.
c.Error(ErrUnauthorized)
c.Header("WWW-Authenticate", realm)
c.AbortWithStatus(http.StatusUnauthorized)
return

View File

@ -137,3 +137,27 @@ func TestBasicAuth401WithCustomRealm(t *testing.T) {
assert.Equal(t, http.StatusUnauthorized, w.Code)
assert.Equal(t, "Basic realm=\"My Custom \\\"Realm\\\"\"", w.Header().Get("WWW-Authenticate"))
}
func TestBasicAuthWithMiddleware(t *testing.T) {
called := false
router := New()
router.Use(func(c *Context) {
called = true
c.Next()
if c.Errors.Last().Err == ErrUnauthorized {
c.JSON(401, H{"message": "Begone!"})
}
}, BasicAuth(Accounts{"foo": "bar"}))
router.GET("/login", func(c *Context) {
c.String(http.StatusOK, c.MustGet(AuthUserKey).(string))
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/login", nil)
req.Header.Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("admin:password")))
router.ServeHTTP(w, req)
assert.True(t, called)
assert.Equal(t, http.StatusUnauthorized, w.Code)
assert.JSONEq(t, `{"message": "Begone!"}`, w.Body.String())
}

View File

@ -46,6 +46,7 @@
- [Redirects](#redirects)
- [Custom Middleware](#custom-middleware)
- [Using BasicAuth() middleware](#using-basicauth-middleware)
- [Detecting authorization failure in custom middleware](#detecting-authorization-failure-in-custom-middleware)
- [Goroutines inside a middleware](#goroutines-inside-a-middleware)
- [Custom HTTP configuration](#custom-http-configuration)
- [Support Let's Encrypt](#support-lets-encrypt)
@ -1468,6 +1469,26 @@ func main() {
}
```
#### Detecting authorization failure in custom middleware
When the `BasicAuth` middleware fails authorization, an `Error` is added to the `gin.Context.Errors` slice. You can detect this failure in a custom middleware with code like this:
```go
func main() {
router := New()
router.Use(func(c *Context) {
c.Next()
if c.Errors.Last().Err == ErrUnauthorized {
// Unauthorized detected, act accordingly
}
})
router.Use(BasicAuth(Accounts{"admin": "password"}))
router.GET("/login", func(c *Context) {
c.String(http.StatusOK, c.MustGet(AuthUserKey).(string))
})
}
```
### Goroutines inside a middleware
When starting new Goroutines inside a middleware or handler, you **SHOULD NOT** use the original context inside it, you have to use a read-only copy.