mirror of
https://github.com/coder/coder.git
synced 2025-07-06 15:41:45 +00:00
feat: Implied 'member' roles for site and organization (#1917)
* feat: Member roles are implied and never exlpicitly added * Rename "GetAllUserRoles" to "GetAuthorizationRoles" * feat: Add migration to remove implied roles * rename user auth role middleware
This commit is contained in:
@ -31,6 +31,19 @@ func APIKey(r *http.Request) database.APIKey {
|
||||
return apiKey
|
||||
}
|
||||
|
||||
// User roles are the 'subject' field of Authorize()
|
||||
type userRolesKey struct{}
|
||||
|
||||
// AuthorizationUserRoles returns the roles used for authorization.
|
||||
// Comes from the ExtractAPIKey handler.
|
||||
func AuthorizationUserRoles(r *http.Request) database.GetAuthorizationUserRolesRow {
|
||||
apiKey, ok := r.Context().Value(userRolesKey{}).(database.GetAuthorizationUserRolesRow)
|
||||
if !ok {
|
||||
panic("developer error: user roles middleware not provided")
|
||||
}
|
||||
return apiKey
|
||||
}
|
||||
|
||||
// OAuth2Configs is a collection of configurations for OAuth-based authentication.
|
||||
// This should be extended to support other authentication types in the future.
|
||||
type OAuth2Configs struct {
|
||||
@ -178,7 +191,7 @@ func ExtractAPIKey(db database.Store, oauth *OAuth2Configs) func(http.Handler) h
|
||||
// If the key is valid, we also fetch the user roles and status.
|
||||
// The roles are used for RBAC authorize checks, and the status
|
||||
// is to block 'suspended' users from accessing the platform.
|
||||
roles, err := db.GetAllUserRoles(r.Context(), key.UserID)
|
||||
roles, err := db.GetAuthorizationUserRoles(r.Context(), key.UserID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
|
||||
Message: "roles not found",
|
||||
|
@ -1,40 +0,0 @@
|
||||
package httpmw
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/coder/coder/coderd/database"
|
||||
"github.com/coder/coder/coderd/httpapi"
|
||||
)
|
||||
|
||||
// User roles are the 'subject' field of Authorize()
|
||||
type userRolesKey struct{}
|
||||
|
||||
// UserRoles returns the API key from the ExtractUserRoles handler.
|
||||
func UserRoles(r *http.Request) database.GetAllUserRolesRow {
|
||||
apiKey, ok := r.Context().Value(userRolesKey{}).(database.GetAllUserRolesRow)
|
||||
if !ok {
|
||||
panic("developer error: user roles middleware not provided")
|
||||
}
|
||||
return apiKey
|
||||
}
|
||||
|
||||
// ExtractUserRoles requires authentication using a valid API key.
|
||||
func ExtractUserRoles(db database.Store) func(http.Handler) http.Handler {
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
apiKey := APIKey(r)
|
||||
role, err := db.GetAllUserRoles(r.Context(), apiKey.UserID)
|
||||
if err != nil {
|
||||
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
|
||||
Message: "roles not found",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := context.WithValue(r.Context(), userRolesKey{}, role)
|
||||
next.ServeHTTP(rw, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
}
|
@ -31,23 +31,23 @@ func TestExtractUserRoles(t *testing.T) {
|
||||
{
|
||||
Name: "Member",
|
||||
AddUser: func(db database.Store) (database.User, []string, string) {
|
||||
roles := []string{rbac.RoleMember()}
|
||||
roles := []string{}
|
||||
user, token := addUser(t, db, roles...)
|
||||
return user, roles, token
|
||||
return user, append(roles, rbac.RoleMember()), token
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Admin",
|
||||
AddUser: func(db database.Store) (database.User, []string, string) {
|
||||
roles := []string{rbac.RoleMember(), rbac.RoleAdmin()}
|
||||
roles := []string{rbac.RoleAdmin()}
|
||||
user, token := addUser(t, db, roles...)
|
||||
return user, roles, token
|
||||
return user, append(roles, rbac.RoleMember()), token
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "OrgMember",
|
||||
AddUser: func(db database.Store) (database.User, []string, string) {
|
||||
roles := []string{rbac.RoleMember()}
|
||||
roles := []string{}
|
||||
user, token := addUser(t, db, roles...)
|
||||
org, err := db.InsertOrganization(context.Background(), database.InsertOrganizationParams{
|
||||
ID: uuid.New(),
|
||||
@ -58,7 +58,7 @@ func TestExtractUserRoles(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
orgRoles := []string{rbac.RoleOrgMember(org.ID)}
|
||||
orgRoles := []string{}
|
||||
_, err = db.InsertOrganizationMember(context.Background(), database.InsertOrganizationMemberParams{
|
||||
OrganizationID: org.ID,
|
||||
UserID: user.ID,
|
||||
@ -67,7 +67,7 @@ func TestExtractUserRoles(t *testing.T) {
|
||||
Roles: orgRoles,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
return user, append(roles, orgRoles...), token
|
||||
return user, append(roles, append(orgRoles, rbac.RoleMember(), rbac.RoleOrgMember(org.ID))...), token
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -86,7 +86,7 @@ func TestExtractUserRoles(t *testing.T) {
|
||||
httpmw.ExtractAPIKey(db, &httpmw.OAuth2Configs{}),
|
||||
)
|
||||
rtr.Get("/", func(_ http.ResponseWriter, r *http.Request) {
|
||||
roles := httpmw.UserRoles(r)
|
||||
roles := httpmw.AuthorizationUserRoles(r)
|
||||
require.ElementsMatch(t, user.ID, roles.ID)
|
||||
require.ElementsMatch(t, expRoles, roles.Roles)
|
||||
})
|
||||
|
Reference in New Issue
Block a user