mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
chore: merge authorization contexts (#12816)
* chore: merge authorization contexts Instead of 2 auth contexts from apikey and dbauthz, merge them to just use dbauthz. It is annoying to have two. * fixup authorization reference
This commit is contained in:
+18
-33
@@ -44,27 +44,15 @@ func APIKey(r *http.Request) database.APIKey {
|
||||
return key
|
||||
}
|
||||
|
||||
// User roles are the 'subject' field of Authorize()
|
||||
type userAuthKey struct{}
|
||||
|
||||
type Authorization struct {
|
||||
Actor rbac.Subject
|
||||
// ActorName is required for logging and human friendly related identification.
|
||||
// It is usually the "username" of the user, but it can be the name of the
|
||||
// external workspace proxy or other service type actor.
|
||||
ActorName string
|
||||
}
|
||||
|
||||
// UserAuthorizationOptional may return the roles and scope used for
|
||||
// authorization. Depends on the ExtractAPIKey handler.
|
||||
func UserAuthorizationOptional(r *http.Request) (Authorization, bool) {
|
||||
auth, ok := r.Context().Value(userAuthKey{}).(Authorization)
|
||||
return auth, ok
|
||||
func UserAuthorizationOptional(r *http.Request) (rbac.Subject, bool) {
|
||||
return dbauthz.ActorFromContext(r.Context())
|
||||
}
|
||||
|
||||
// UserAuthorization returns the roles and scope used for authorization. Depends
|
||||
// on the ExtractAPIKey handler.
|
||||
func UserAuthorization(r *http.Request) Authorization {
|
||||
func UserAuthorization(r *http.Request) rbac.Subject {
|
||||
auth, ok := UserAuthorizationOptional(r)
|
||||
if !ok {
|
||||
panic("developer error: ExtractAPIKey middleware not provided")
|
||||
@@ -119,7 +107,7 @@ type ExtractAPIKeyConfig struct {
|
||||
//
|
||||
// This is originally implemented to send entitlement warning headers after
|
||||
// a user is authenticated to prevent additional CLI invocations.
|
||||
PostAuthAdditionalHeadersFunc func(a Authorization, header http.Header)
|
||||
PostAuthAdditionalHeadersFunc func(a rbac.Subject, header http.Header)
|
||||
}
|
||||
|
||||
// ExtractAPIKeyMW calls ExtractAPIKey with the given config on each request,
|
||||
@@ -142,9 +130,8 @@ func ExtractAPIKeyMW(cfg ExtractAPIKeyConfig) func(http.Handler) http.Handler {
|
||||
// Actor is the user's authorization context.
|
||||
ctx := r.Context()
|
||||
ctx = context.WithValue(ctx, apiKeyContextKey{}, key)
|
||||
ctx = context.WithValue(ctx, userAuthKey{}, authz)
|
||||
// Set the auth context for the authzquerier as well.
|
||||
ctx = dbauthz.As(ctx, authz.Actor)
|
||||
// Set the auth context for the user.
|
||||
ctx = dbauthz.As(ctx, authz)
|
||||
|
||||
next.ServeHTTP(rw, r.WithContext(ctx))
|
||||
})
|
||||
@@ -209,12 +196,12 @@ func APIKeyFromRequest(ctx context.Context, db database.Store, sessionTokenFunc
|
||||
// and authz object may be returned. False is returned if a response was written
|
||||
// to the request and the caller should give up.
|
||||
// nolint:revive
|
||||
func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyConfig) (*database.APIKey, *Authorization, bool) {
|
||||
func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyConfig) (*database.APIKey, *rbac.Subject, bool) {
|
||||
ctx := r.Context()
|
||||
// Write wraps writing a response to redirect if the handler
|
||||
// specified it should. This redirect is used for user-facing pages
|
||||
// like workspace applications.
|
||||
write := func(code int, response codersdk.Response) (*database.APIKey, *Authorization, bool) {
|
||||
write := func(code int, response codersdk.Response) (*database.APIKey, *rbac.Subject, bool) {
|
||||
if cfg.RedirectToLogin {
|
||||
RedirectToLogin(rw, r, nil, response.Message)
|
||||
return nil, nil, false
|
||||
@@ -229,7 +216,7 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
|
||||
//
|
||||
// It should be used when the API key is not provided or is invalid,
|
||||
// but not when there are other errors.
|
||||
optionalWrite := func(code int, response codersdk.Response) (*database.APIKey, *Authorization, bool) {
|
||||
optionalWrite := func(code int, response codersdk.Response) (*database.APIKey, *rbac.Subject, bool) {
|
||||
if cfg.Optional {
|
||||
return nil, nil, true
|
||||
}
|
||||
@@ -451,21 +438,19 @@ func ExtractAPIKey(rw http.ResponseWriter, r *http.Request, cfg ExtractAPIKeyCon
|
||||
}
|
||||
|
||||
// Actor is the user's authorization context.
|
||||
authz := Authorization{
|
||||
ActorName: roles.Username,
|
||||
Actor: rbac.Subject{
|
||||
ID: key.UserID.String(),
|
||||
Roles: rbac.RoleNames(roles.Roles),
|
||||
Groups: roles.Groups,
|
||||
Scope: rbac.ScopeName(key.Scope),
|
||||
}.WithCachedASTValue(),
|
||||
}
|
||||
actor := rbac.Subject{
|
||||
FriendlyName: roles.Username,
|
||||
ID: key.UserID.String(),
|
||||
Roles: rbac.RoleNames(roles.Roles),
|
||||
Groups: roles.Groups,
|
||||
Scope: rbac.ScopeName(key.Scope),
|
||||
}.WithCachedASTValue()
|
||||
|
||||
if cfg.PostAuthAdditionalHeadersFunc != nil {
|
||||
cfg.PostAuthAdditionalHeadersFunc(authz, rw.Header())
|
||||
cfg.PostAuthAdditionalHeadersFunc(actor, rw.Header())
|
||||
}
|
||||
|
||||
return key, &authz, true
|
||||
return key, &actor, true
|
||||
}
|
||||
|
||||
// APITokenFromRequest returns the api token from the request.
|
||||
|
||||
@@ -127,8 +127,8 @@ func TestExtractUserRoles(t *testing.T) {
|
||||
)
|
||||
rtr.Get("/", func(_ http.ResponseWriter, r *http.Request) {
|
||||
roles := httpmw.UserAuthorization(r)
|
||||
require.Equal(t, user.ID.String(), roles.Actor.ID)
|
||||
require.ElementsMatch(t, expRoles, roles.Actor.Roles.Names())
|
||||
require.Equal(t, user.ID.String(), roles.ID)
|
||||
require.ElementsMatch(t, expRoles, roles.Roles.Names())
|
||||
})
|
||||
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
|
||||
@@ -5,8 +5,6 @@ import (
|
||||
"crypto/subtle"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
@@ -68,18 +66,6 @@ func ExtractProvisionerDaemonAuthenticated(opts ExtractProvisionerAuthConfig, ps
|
||||
ctx = context.WithValue(ctx, provisionerDaemonContextKey{}, true)
|
||||
// nolint:gocritic // Authenticating as a provisioner daemon.
|
||||
ctx = dbauthz.AsProvisionerd(ctx)
|
||||
subj, ok := dbauthz.ActorFromContext(ctx)
|
||||
if !ok {
|
||||
// This should never happen
|
||||
httpapi.InternalServerError(w, xerrors.New("developer error: ExtractProvisionerDaemonAuth missing rbac actor"))
|
||||
}
|
||||
|
||||
// Use the same subject for the userAuthKey
|
||||
ctx = context.WithValue(ctx, userAuthKey{}, Authorization{
|
||||
Actor: subj,
|
||||
ActorName: "provisioner_daemon",
|
||||
})
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func RateLimit(count int, window time.Duration) func(http.Handler) http.Handler
|
||||
|
||||
// We avoid using rbac.Authorizer since rego is CPU-intensive
|
||||
// and undermines the DoS-prevention goal of the rate limiter.
|
||||
for _, role := range auth.Actor.SafeRoleNames() {
|
||||
for _, role := range auth.SafeRoleNames() {
|
||||
if role == rbac.RoleOwner() {
|
||||
// HACK: use a random key each time to
|
||||
// de facto disable rate limiting. The
|
||||
|
||||
@@ -141,18 +141,6 @@ func ExtractWorkspaceProxy(opts ExtractWorkspaceProxyConfig) func(http.Handler)
|
||||
// they can still only access the routes that the middleware is
|
||||
// mounted to.
|
||||
ctx = dbauthz.AsSystemRestricted(ctx)
|
||||
subj, ok := dbauthz.ActorFromContext(ctx)
|
||||
if !ok {
|
||||
// This should never happen
|
||||
httpapi.InternalServerError(w, xerrors.New("developer error: ExtractWorkspaceProxy missing rbac actor"))
|
||||
return
|
||||
}
|
||||
// Use the same subject for the userAuthKey
|
||||
ctx = context.WithValue(ctx, userAuthKey{}, Authorization{
|
||||
Actor: subj,
|
||||
ActorName: "proxy_" + proxy.Name,
|
||||
})
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user