/
auth.go
83 lines (70 loc) · 2.57 KB
/
auth.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Copyright 2018 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package httpcontext
import (
"context"
"fmt"
"net/http"
"github.com/juju/errors"
"github.com/juju/loggo"
"github.com/juju/juju/apiserver/authentication"
apiservererrors "github.com/juju/juju/apiserver/errors"
)
// StrategicAuthenticator is responsible for trying multiple Authenticators
// until one succeeds or an error is returned that is not equal to NotFound.
type HTTPStrategicAuthenticator []authentication.HTTPAuthenticator
// AuthHandler is a http handler responsible for handling authz and authn for
// http requests coming into Juju. If a request both authenticates and authorizes
// then the authentication info is also padded into the http context and the
// next http handler is called.
type AuthHandler struct {
// NextHandler is the http handler to call after authentication has been
// completed.
NextHandler http.Handler
// Authenticator is the Authenticator used for authenticating
// the HTTP requests handled by this handler.
Authenticator authentication.HTTPAuthenticator
// Authorizer, if non-nil, will be called with the auth info
// returned by the Authenticator, to validate it for the route.
Authorizer authentication.Authorizer
}
var logger = loggo.GetLogger("juju.apiserver.httpcontext")
// ServeHTTP is part of the http.Handler interface and is responsible for
// performing AuthN and AuthZ on the subsequent http request.
func (h *AuthHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
authInfo, err := h.Authenticator.Authenticate(req)
if err != nil {
var httpError apiservererrors.HTTPWritableError
if errors.As(err, &httpError) {
if err := httpError.SendError(w); err != nil {
logger.Warningf("failed sending http error %v", err)
}
} else {
http.Error(w,
fmt.Sprintf("authentication failed: %s", err),
http.StatusUnauthorized,
)
}
return
}
if h.Authorizer != nil {
if err := h.Authorizer.Authorize(authInfo); err != nil {
http.Error(w,
fmt.Sprintf("authorization failed: %s", err),
http.StatusForbidden,
)
return
}
}
ctx := context.WithValue(req.Context(), authInfoKey{}, authInfo)
req = req.WithContext(ctx)
h.NextHandler.ServeHTTP(w, req)
}
type authInfoKey struct{}
// RequestAuthInfo returns the AuthInfo associated with the request,
// if any, and a boolean indicating whether or not the request was
// authenticated.
func RequestAuthInfo(req *http.Request) (authentication.AuthInfo, bool) {
authInfo, ok := req.Context().Value(authInfoKey{}).(authentication.AuthInfo)
return authInfo, ok
}