feat: Add initial AuthzQuerier implementation (#5919)

feat: Add initial AuthzQuerier implementation
- Adds package database/dbauthz that adds a database.Store implementation where each method goes through AuthZ checks
- Implements all database.Store methods on AuthzQuerier
- Updates and fixes unit tests where required
- Updates coderd initialization to use AuthzQuerier if codersdk.ExperimentAuthzQuerier is enabled
This commit is contained in:
Steven Masley
2023-02-14 08:27:06 -06:00
committed by GitHub
parent ebdfdc749d
commit 6fb8aff6d0
59 changed files with 5013 additions and 136 deletions

View File

@ -18,9 +18,9 @@ import (
"golang.org/x/xerrors"
"cdr.dev/slog"
"github.com/coder/coder/coderd/audit"
"github.com/coder/coder/coderd/database"
"github.com/coder/coder/coderd/database/dbauthz"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/rbac"
@ -57,7 +57,8 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
return
}
user, err := api.Database.GetUserByEmailOrUsername(ctx, database.GetUserByEmailOrUsernameParams{
//nolint:gocritic // In order to login, we need to get the user first!
user, err := api.Database.GetUserByEmailOrUsername(dbauthz.AsSystem(ctx), database.GetUserByEmailOrUsernameParams{
Email: loginWithPassword.Email,
})
if err != nil && !xerrors.Is(err, sql.ErrNoRows) {
@ -111,15 +112,32 @@ func (api *API) postLogin(rw http.ResponseWriter, r *http.Request) {
return
}
//nolint:gocritic // System needs to fetch user roles in order to login user.
roles, err := api.Database.GetAuthorizationUserRoles(dbauthz.AsSystem(ctx), user.ID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error.",
})
return
}
// If the user logged into a suspended account, reject the login request.
if user.Status != database.UserStatusActive {
if roles.Status != database.UserStatusActive {
httpapi.Write(ctx, rw, http.StatusUnauthorized, codersdk.Response{
Message: "Your account is suspended. Contact an admin to reactivate your account.",
})
return
}
cookie, key, err := api.createAPIKey(ctx, createAPIKeyParams{
userSubj := rbac.Subject{
ID: user.ID.String(),
Roles: rbac.RoleNames(roles.Roles),
Groups: roles.Groups,
Scope: rbac.ScopeAll,
}
//nolint:gocritic // Creating the API key as the user instead of as system.
cookie, key, err := api.createAPIKey(dbauthz.As(ctx, userSubj), createAPIKeyParams{
UserID: user.ID,
LoginType: database.LoginTypePassword,
RemoteAddr: r.RemoteAddr,
@ -765,7 +783,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
// with OIDC for the first time.
if user.ID == uuid.Nil {
var organizationID uuid.UUID
organizations, _ := tx.GetOrganizations(ctx)
//nolint:gocritic
organizations, _ := tx.GetOrganizations(dbauthz.AsSystem(ctx))
if len(organizations) > 0 {
// Add the user to the first organization. Once multi-organization
// support is added, we should enable a configuration map of user
@ -773,7 +792,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
organizationID = organizations[0].ID
}
_, err := tx.GetUserByEmailOrUsername(ctx, database.GetUserByEmailOrUsernameParams{
//nolint:gocritic
_, err := tx.GetUserByEmailOrUsername(dbauthz.AsSystem(ctx), database.GetUserByEmailOrUsernameParams{
Username: params.Username,
})
if err == nil {
@ -786,7 +806,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
params.Username = httpapi.UsernameFrom(alternate)
_, err := tx.GetUserByEmailOrUsername(ctx, database.GetUserByEmailOrUsernameParams{
//nolint:gocritic
_, err := tx.GetUserByEmailOrUsername(dbauthz.AsSystem(ctx), database.GetUserByEmailOrUsernameParams{
Username: params.Username,
})
if xerrors.Is(err, sql.ErrNoRows) {
@ -805,7 +826,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
}
}
user, _, err = api.CreateUser(ctx, tx, CreateUserRequest{
//nolint:gocritic
user, _, err = api.CreateUser(dbauthz.AsSystem(ctx), tx, CreateUserRequest{
CreateUserRequest: codersdk.CreateUserRequest{
Email: params.Email,
Username: params.Username,
@ -819,7 +841,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
}
if link.UserID == uuid.Nil {
link, err = tx.InsertUserLink(ctx, database.InsertUserLinkParams{
//nolint:gocritic
link, err = tx.InsertUserLink(dbauthz.AsSystem(ctx), database.InsertUserLinkParams{
UserID: user.ID,
LoginType: params.LoginType,
LinkedID: params.LinkedID,
@ -833,7 +856,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
}
if link.UserID != uuid.Nil {
link, err = tx.UpdateUserLink(ctx, database.UpdateUserLinkParams{
//nolint:gocritic
link, err = tx.UpdateUserLink(dbauthz.AsSystem(ctx), database.UpdateUserLinkParams{
UserID: user.ID,
LoginType: params.LoginType,
OAuthAccessToken: params.State.Token.AccessToken,
@ -847,7 +871,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
// Ensure groups are correct.
if len(params.Groups) > 0 {
err := api.Options.SetUserGroups(ctx, tx, user.ID, params.Groups)
//nolint:gocritic
err := api.Options.SetUserGroups(dbauthz.AsSystem(ctx), tx, user.ID, params.Groups)
if err != nil {
return xerrors.Errorf("set user groups: %w", err)
}
@ -880,7 +905,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
// In such cases in the current implementation this user can now no
// longer sign in until an administrator finds the offending built-in
// user and changes their username.
user, err = tx.UpdateUserProfile(ctx, database.UpdateUserProfileParams{
//nolint:gocritic
user, err = tx.UpdateUserProfile(dbauthz.AsSystem(ctx), database.UpdateUserProfileParams{
ID: user.ID,
Email: user.Email,
Username: user.Username,
@ -898,7 +924,8 @@ func (api *API) oauthLogin(r *http.Request, params oauthLoginParams) (*http.Cook
return nil, database.APIKey{}, xerrors.Errorf("in tx: %w", err)
}
cookie, key, err := api.createAPIKey(ctx, createAPIKeyParams{
//nolint:gocritic
cookie, key, err := api.createAPIKey(dbauthz.AsSystem(ctx), createAPIKeyParams{
UserID: user.ID,
LoginType: params.LoginType,
RemoteAddr: r.RemoteAddr,