gateway: initial authorization

This commit is contained in:
Simone Gotti 2019-05-03 23:19:23 +02:00
parent 081ac8a44f
commit a04dd62e91
11 changed files with 500 additions and 34 deletions

View File

@ -0,0 +1,242 @@
// Copyright 2019 Sorint.lab
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied
// See the License for the specific language governing permissions and
// limitations under the License.
package action
import (
"context"
"github.com/sorintlab/agola/internal/services/gateway/common"
"github.com/sorintlab/agola/internal/services/types"
"github.com/pkg/errors"
)
func (h *ActionHandler) CurrentUserID(ctx context.Context) string {
userIDVal := ctx.Value("userid")
if userIDVal == nil {
return ""
}
return userIDVal.(string)
}
func (h *ActionHandler) IsUserLogged(ctx context.Context) bool {
return ctx.Value("userid") != nil
}
func (h *ActionHandler) IsUserAdmin(ctx context.Context) bool {
isAdmin := false
isAdminVal := ctx.Value("admin")
if isAdminVal != nil {
isAdmin = isAdminVal.(bool)
}
return isAdmin
}
func (h *ActionHandler) IsUserLoggedOrAdmin(ctx context.Context) bool {
return h.IsUserLogged(ctx) || h.IsUserAdmin(ctx)
}
func (h *ActionHandler) IsOrgOwner(ctx context.Context, orgID string) (bool, error) {
isAdmin := h.IsUserAdmin(ctx)
if isAdmin {
return true, nil
}
userID := h.CurrentUserID(ctx)
if userID == "" {
return false, nil
}
userOrgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userID)
if err != nil {
return false, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user orgs"))
}
for _, userOrg := range userOrgs {
if userOrg.Organization.ID != orgID {
continue
}
if userOrg.Role == types.MemberRoleOwner {
return true, nil
}
}
return false, nil
}
func (h *ActionHandler) IsProjectOwner(ctx context.Context, ownerType types.ConfigType, ownerID string) (bool, error) {
isAdmin := h.IsUserAdmin(ctx)
if isAdmin {
return true, nil
}
userID := h.CurrentUserID(ctx)
if userID == "" {
return false, nil
}
if ownerType == types.ConfigTypeUser {
if userID == ownerID {
return true, nil
}
}
if ownerType == types.ConfigTypeOrg {
userOrgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userID)
if err != nil {
return false, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user orgs"))
}
for _, userOrg := range userOrgs {
if userOrg.Organization.ID != ownerID {
continue
}
if userOrg.Role == types.MemberRoleOwner {
return true, nil
}
}
}
return false, nil
}
func (h *ActionHandler) IsProjectMember(ctx context.Context, ownerType types.ConfigType, ownerID string) (bool, error) {
isAdmin := h.IsUserAdmin(ctx)
if isAdmin {
return true, nil
}
userID := h.CurrentUserID(ctx)
if userID == "" {
return false, nil
}
if ownerType == types.ConfigTypeUser {
if userID == ownerID {
return true, nil
}
}
if ownerType == types.ConfigTypeOrg {
userOrgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userID)
if err != nil {
return false, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user orgs"))
}
for _, userOrg := range userOrgs {
if userOrg.Organization.ID != ownerID {
continue
}
if userOrg.Role == types.MemberRoleMember {
return true, nil
}
}
}
return false, nil
}
func (h *ActionHandler) IsVariableOwner(ctx context.Context, parentType types.ConfigType, parentRef string) (bool, error) {
var ownerType types.ConfigType
var ownerID string
switch parentType {
case types.ConfigTypeProjectGroup:
pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, parentRef)
if err != nil {
return false, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project group %q", parentRef))
}
ownerType = pg.OwnerType
ownerID = pg.OwnerID
case types.ConfigTypeProject:
p, resp, err := h.configstoreClient.GetProject(ctx, parentRef)
if err != nil {
return false, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project %q", parentRef))
}
ownerType = p.OwnerType
ownerID = p.OwnerID
}
return h.IsProjectOwner(ctx, ownerType, ownerID)
}
func (h *ActionHandler) CanGetRun(ctx context.Context, runGroup string) (bool, error) {
groupType, groupID, err := common.GroupTypeIDFromRunGroup(runGroup)
if err != nil {
return false, err
}
var visibility types.Visibility
var ownerType types.ConfigType
var ownerID string
switch groupType {
case common.GroupTypeProject:
p, resp, err := h.configstoreClient.GetProject(ctx, groupID)
if err != nil {
return false, ErrFromRemote(resp, err)
}
ownerType = p.OwnerType
ownerID = p.OwnerID
visibility = p.GlobalVisibility
case common.GroupTypeUser:
// user local runs
ownerType = types.ConfigTypeUser
ownerID = groupID
visibility = types.VisibilityPrivate
}
isProjectMember, err := h.IsProjectMember(ctx, ownerType, ownerID)
if err != nil {
return false, errors.Wrapf(err, "failed to determine ownership")
}
if visibility == types.VisibilityPublic {
return true, nil
}
if !isProjectMember {
return false, nil
}
return true, nil
}
func (h *ActionHandler) CanDoRunActions(ctx context.Context, runGroup string) (bool, error) {
groupType, groupID, err := common.GroupTypeIDFromRunGroup(runGroup)
if err != nil {
return false, err
}
var ownerType types.ConfigType
var ownerID string
switch groupType {
case common.GroupTypeProject:
p, resp, err := h.configstoreClient.GetProject(ctx, groupID)
if err != nil {
return false, ErrFromRemote(resp, err)
}
ownerType = p.OwnerType
ownerID = p.OwnerID
case common.GroupTypeUser:
// user local runs
ownerType = types.ConfigTypeUser
ownerID = groupID
}
isProjectOwner, err := h.IsProjectOwner(ctx, ownerType, ownerID)
if err != nil {
return false, errors.Wrapf(err, "failed to determine ownership")
}
if !isProjectOwner {
return false, nil
}
return true, nil
}

View File

@ -52,6 +52,10 @@ type CreateOrgRequest struct {
} }
func (h *ActionHandler) CreateOrg(ctx context.Context, req *CreateOrgRequest) (*types.Organization, error) { func (h *ActionHandler) CreateOrg(ctx context.Context, req *CreateOrgRequest) (*types.Organization, error) {
if !h.IsUserLoggedOrAdmin(ctx) {
return nil, errors.Errorf("user not logged in")
}
if req.Name == "" { if req.Name == "" {
return nil, util.NewErrBadRequest(errors.Errorf("organization name required")) return nil, util.NewErrBadRequest(errors.Errorf("organization name required"))
} }
@ -77,7 +81,20 @@ func (h *ActionHandler) CreateOrg(ctx context.Context, req *CreateOrgRequest) (*
} }
func (h *ActionHandler) DeleteOrg(ctx context.Context, orgRef string) error { func (h *ActionHandler) DeleteOrg(ctx context.Context, orgRef string) error {
resp, err := h.configstoreClient.DeleteOrg(ctx, orgRef) org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef)
if err != nil {
return ErrFromRemote(resp, err)
}
isOrgOwner, err := h.IsOrgOwner(ctx, org.ID)
if err != nil {
return errors.Wrapf(err, "failed to determine ownership")
}
if !isOrgOwner {
return util.NewErrForbidden(errors.Errorf("user not authorized"))
}
resp, err = h.configstoreClient.DeleteOrg(ctx, orgRef)
if err != nil { if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete org")) return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete org"))
} }

View File

@ -33,6 +33,18 @@ func (h *ActionHandler) GetProject(ctx context.Context, projectRef string) (*csa
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, err) return nil, ErrFromRemote(resp, err)
} }
isProjectMember, err := h.IsProjectMember(ctx, project.OwnerType, project.OwnerID)
if err != nil {
return nil, errors.Wrapf(err, "failed to determine ownership")
}
if project.GlobalVisibility == types.VisibilityPublic {
return project, nil
}
if !isProjectMember {
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
return project, nil return project, nil
} }
@ -47,6 +59,29 @@ type CreateProjectRequest struct {
} }
func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectRequest) (*csapi.Project, error) { func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectRequest) (*csapi.Project, error) {
user, resp, err := h.configstoreClient.GetUser(ctx, req.CurrentUserID)
if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", req.CurrentUserID))
}
parentRef := req.ParentRef
if parentRef == "" {
// create project in current user namespace
parentRef = path.Join("user", user.Name)
}
pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, parentRef)
if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project group %q", parentRef))
}
isProjectOwner, err := h.IsProjectOwner(ctx, pg.OwnerType, pg.OwnerID)
if err != nil {
return nil, errors.Wrapf(err, "failed to determine ownership")
}
if !isProjectOwner {
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
if !util.ValidateName(req.Name) { if !util.ValidateName(req.Name) {
return nil, util.NewErrBadRequest(errors.Errorf("invalid project name %q", req.Name)) return nil, util.NewErrBadRequest(errors.Errorf("invalid project name %q", req.Name))
} }
@ -57,11 +92,6 @@ func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectReq
return nil, util.NewErrBadRequest(errors.Errorf("empty remote repo path")) return nil, util.NewErrBadRequest(errors.Errorf("empty remote repo path"))
} }
pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, req.ParentRef)
if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project group %q", req.Name))
}
projectPath := path.Join(pg.Path, req.Name) projectPath := path.Join(pg.Path, req.Name)
_, resp, err = h.configstoreClient.GetProject(ctx, projectPath) _, resp, err = h.configstoreClient.GetProject(ctx, projectPath)
if err != nil { if err != nil {
@ -72,11 +102,6 @@ func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectReq
return nil, util.NewErrBadRequest(errors.Errorf("project %q already exists", projectPath)) return nil, util.NewErrBadRequest(errors.Errorf("project %q already exists", projectPath))
} }
user, resp, err := h.configstoreClient.GetUser(ctx, req.CurrentUserID)
if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", req.CurrentUserID))
}
rs, resp, err := h.configstoreClient.GetRemoteSource(ctx, req.RemoteSourceName) rs, resp, err := h.configstoreClient.GetRemoteSource(ctx, req.RemoteSourceName)
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get remote source %q", req.RemoteSourceName)) return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get remote source %q", req.RemoteSourceName))
@ -110,12 +135,6 @@ func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectReq
return nil, errors.Wrapf(err, "failed to generate ssh key pair") return nil, errors.Wrapf(err, "failed to generate ssh key pair")
} }
parentRef := req.ParentRef
if parentRef == "" {
// create project in current user namespace
parentRef = path.Join("user", user.Name)
}
p := &types.Project{ p := &types.Project{
Name: req.Name, Name: req.Name,
Parent: types.Parent{ Parent: types.Parent{
@ -188,6 +207,14 @@ func (h *ActionHandler) ReconfigProject(ctx context.Context, projectRef string)
return ErrFromRemote(resp, errors.Wrapf(err, "failed to get project %q", projectRef)) return ErrFromRemote(resp, errors.Wrapf(err, "failed to get project %q", projectRef))
} }
isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID)
if err != nil {
return errors.Wrapf(err, "failed to determine ownership")
}
if !isProjectOwner {
return util.NewErrForbidden(errors.Errorf("user not authorized"))
}
user, resp, err := h.configstoreClient.GetUserByLinkedAccount(ctx, p.LinkedAccountID) user, resp, err := h.configstoreClient.GetUserByLinkedAccount(ctx, p.LinkedAccountID)
if err != nil { if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to get user with linked account id %q", p.LinkedAccountID)) return ErrFromRemote(resp, errors.Wrapf(err, "failed to get user with linked account id %q", p.LinkedAccountID))
@ -210,7 +237,20 @@ func (h *ActionHandler) ReconfigProject(ctx context.Context, projectRef string)
} }
func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) error { func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) error {
resp, err := h.configstoreClient.DeleteProject(ctx, projectRef) p, resp, err := h.configstoreClient.GetProject(ctx, projectRef)
if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to get project %q", projectRef))
}
isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID)
if err != nil {
return errors.Wrapf(err, "failed to determine ownership")
}
if !isProjectOwner {
return util.NewErrForbidden(errors.Errorf("user not authorized"))
}
resp, err = h.configstoreClient.DeleteProject(ctx, projectRef)
if err != nil { if err != nil {
return ErrFromRemote(resp, err) return ErrFromRemote(resp, err)
} }

View File

@ -61,6 +61,19 @@ func (h *ActionHandler) CreateProjectGroup(ctx context.Context, req *CreateProje
return nil, util.NewErrBadRequest(errors.Errorf("invalid projectGroup name %q", req.Name)) return nil, util.NewErrBadRequest(errors.Errorf("invalid projectGroup name %q", req.Name))
} }
pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, req.ParentRef)
if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get project group %q", req.ParentRef))
}
isProjectOwner, err := h.IsProjectOwner(ctx, pg.OwnerType, pg.OwnerID)
if err != nil {
return nil, errors.Wrapf(err, "failed to determine ownership")
}
if !isProjectOwner {
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
user, resp, err := h.configstoreClient.GetUser(ctx, req.CurrentUserID) user, resp, err := h.configstoreClient.GetUser(ctx, req.CurrentUserID)
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", req.CurrentUserID)) return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", req.CurrentUserID))

View File

@ -55,6 +55,10 @@ type CreateRemoteSourceRequest struct {
} }
func (h *ActionHandler) CreateRemoteSource(ctx context.Context, req *CreateRemoteSourceRequest) (*types.RemoteSource, error) { func (h *ActionHandler) CreateRemoteSource(ctx context.Context, req *CreateRemoteSourceRequest) (*types.RemoteSource, error) {
if !h.IsUserAdmin(ctx) {
return nil, errors.Errorf("user not admin")
}
if !util.ValidateName(req.Name) { if !util.ValidateName(req.Name) {
return nil, util.NewErrBadRequest(errors.Errorf("invalid remotesource name %q", req.Name)) return nil, util.NewErrBadRequest(errors.Errorf("invalid remotesource name %q", req.Name))
} }
@ -106,6 +110,10 @@ func (h *ActionHandler) CreateRemoteSource(ctx context.Context, req *CreateRemot
} }
func (h *ActionHandler) DeleteRemoteSource(ctx context.Context, rsRef string) error { func (h *ActionHandler) DeleteRemoteSource(ctx context.Context, rsRef string) error {
if !h.IsUserAdmin(ctx) {
return errors.Errorf("user not admin")
}
resp, err := h.configstoreClient.DeleteRemoteSource(ctx, rsRef) resp, err := h.configstoreClient.DeleteRemoteSource(ctx, rsRef)
if err != nil { if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete remote source")) return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete remote source"))

View File

@ -30,12 +30,20 @@ func (h *ActionHandler) GetRun(ctx context.Context, runID string) (*rsapi.RunRes
return nil, ErrFromRemote(resp, err) return nil, ErrFromRemote(resp, err)
} }
canGetRun, err := h.CanGetRun(ctx, runResp.RunConfig.Group)
if err != nil {
return nil, errors.Wrapf(err, "failed to determine permissions")
}
if !canGetRun {
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
return runResp, nil return runResp, nil
} }
type GetRunsRequest struct { type GetRunsRequest struct {
PhaseFilter []string PhaseFilter []string
Groups []string Group string
LastRun bool LastRun bool
ChangeGroups []string ChangeGroups []string
StartRunID string StartRunID string
@ -44,7 +52,16 @@ type GetRunsRequest struct {
} }
func (h *ActionHandler) GetRuns(ctx context.Context, req *GetRunsRequest) (*rsapi.GetRunsResponse, error) { func (h *ActionHandler) GetRuns(ctx context.Context, req *GetRunsRequest) (*rsapi.GetRunsResponse, error) {
runsResp, resp, err := h.runserviceClient.GetRuns(ctx, req.PhaseFilter, req.Groups, req.LastRun, req.ChangeGroups, req.StartRunID, req.Limit, req.Asc) canGetRun, err := h.CanGetRun(ctx, req.Group)
if err != nil {
return nil, errors.Wrapf(err, "failed to determine permissions")
}
if !canGetRun {
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
groups := []string{req.Group}
runsResp, resp, err := h.runserviceClient.GetRuns(ctx, req.PhaseFilter, groups, req.LastRun, req.ChangeGroups, req.StartRunID, req.Limit, req.Asc)
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, err) return nil, ErrFromRemote(resp, err)
} }
@ -62,7 +79,19 @@ type GetLogsRequest struct {
} }
func (h *ActionHandler) GetLogs(ctx context.Context, req *GetLogsRequest) (*http.Response, error) { func (h *ActionHandler) GetLogs(ctx context.Context, req *GetLogsRequest) (*http.Response, error) {
resp, err := h.runserviceClient.GetLogs(ctx, req.RunID, req.TaskID, req.Setup, req.Step, req.Follow, req.Stream) runResp, resp, err := h.runserviceClient.GetRun(ctx, req.RunID)
if err != nil {
return nil, ErrFromRemote(resp, err)
}
canGetRun, err := h.CanGetRun(ctx, runResp.RunConfig.Group)
if err != nil {
return nil, errors.Wrapf(err, "failed to determine permissions")
}
if !canGetRun {
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
resp, err = h.runserviceClient.GetLogs(ctx, req.RunID, req.TaskID, req.Setup, req.Step, req.Follow, req.Stream)
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, err) return nil, ErrFromRemote(resp, err)
} }
@ -86,6 +115,18 @@ type RunActionsRequest struct {
} }
func (h *ActionHandler) RunAction(ctx context.Context, req *RunActionsRequest) error { func (h *ActionHandler) RunAction(ctx context.Context, req *RunActionsRequest) error {
runResp, resp, err := h.runserviceClient.GetRun(ctx, req.RunID)
if err != nil {
return ErrFromRemote(resp, err)
}
canGetRun, err := h.CanDoRunActions(ctx, runResp.RunConfig.Group)
if err != nil {
return errors.Wrapf(err, "failed to determine permissions")
}
if !canGetRun {
return util.NewErrForbidden(errors.Errorf("user not authorized"))
}
switch req.ActionType { switch req.ActionType {
case RunActionTypeRestart: case RunActionTypeRestart:
rsreq := &rsapi.RunCreateRequest{ rsreq := &rsapi.RunCreateRequest{
@ -130,6 +171,18 @@ type RunTaskActionsRequest struct {
} }
func (h *ActionHandler) RunTaskAction(ctx context.Context, req *RunTaskActionsRequest) error { func (h *ActionHandler) RunTaskAction(ctx context.Context, req *RunTaskActionsRequest) error {
runResp, resp, err := h.runserviceClient.GetRun(ctx, req.RunID)
if err != nil {
return ErrFromRemote(resp, err)
}
canGetRun, err := h.CanDoRunActions(ctx, runResp.RunConfig.Group)
if err != nil {
return errors.Wrapf(err, "failed to determine permissions")
}
if !canGetRun {
return util.NewErrForbidden(errors.Errorf("user not authorized"))
}
switch req.ActionType { switch req.ActionType {
case RunTaskActionTypeApprove: case RunTaskActionTypeApprove:
rsreq := &rsapi.RunTaskActionsRequest{ rsreq := &rsapi.RunTaskActionsRequest{

View File

@ -72,6 +72,14 @@ type CreateSecretHandler struct {
} }
func (h *ActionHandler) CreateSecret(ctx context.Context, req *CreateSecretRequest) (*csapi.Secret, error) { func (h *ActionHandler) CreateSecret(ctx context.Context, req *CreateSecretRequest) (*csapi.Secret, error) {
isVariableOwner, err := h.IsVariableOwner(ctx, req.ParentType, req.ParentRef)
if err != nil {
return nil, errors.Wrapf(err, "failed to determine ownership")
}
if !isVariableOwner {
return nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
if !util.ValidateName(req.Name) { if !util.ValidateName(req.Name) {
return nil, util.NewErrBadRequest(errors.Errorf("invalid secret name %q", req.Name)) return nil, util.NewErrBadRequest(errors.Errorf("invalid secret name %q", req.Name))
} }
@ -84,7 +92,6 @@ func (h *ActionHandler) CreateSecret(ctx context.Context, req *CreateSecretReque
var resp *http.Response var resp *http.Response
var rs *csapi.Secret var rs *csapi.Secret
var err error
switch req.ParentType { switch req.ParentType {
case types.ConfigTypeProjectGroup: case types.ConfigTypeProjectGroup:
h.log.Infof("creating project group secret") h.log.Infof("creating project group secret")
@ -102,8 +109,15 @@ func (h *ActionHandler) CreateSecret(ctx context.Context, req *CreateSecretReque
} }
func (h *ActionHandler) DeleteSecret(ctx context.Context, parentType types.ConfigType, parentRef, name string) error { func (h *ActionHandler) DeleteSecret(ctx context.Context, parentType types.ConfigType, parentRef, name string) error {
isVariableOwner, err := h.IsVariableOwner(ctx, parentType, parentRef)
if err != nil {
return errors.Wrapf(err, "failed to determine ownership")
}
if !isVariableOwner {
return util.NewErrForbidden(errors.Errorf("user not authorized"))
}
var resp *http.Response var resp *http.Response
var err error
switch parentType { switch parentType {
case types.ConfigTypeProjectGroup: case types.ConfigTypeProjectGroup:
h.log.Infof("deleting project group secret") h.log.Infof("deleting project group secret")

View File

@ -41,6 +41,10 @@ func isAccessTokenExpired(expiresAt time.Time) bool {
} }
func (h *ActionHandler) GetUser(ctx context.Context, userRef string) (*types.User, error) { func (h *ActionHandler) GetUser(ctx context.Context, userRef string) (*types.User, error) {
if !h.IsUserLoggedOrAdmin(ctx) {
return nil, errors.Errorf("user not logged in")
}
user, resp, err := h.configstoreClient.GetUser(ctx, userRef) user, resp, err := h.configstoreClient.GetUser(ctx, userRef)
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, err) return nil, ErrFromRemote(resp, err)
@ -55,6 +59,10 @@ type GetUsersRequest struct {
} }
func (h *ActionHandler) GetUsers(ctx context.Context, req *GetUsersRequest) ([]*types.User, error) { func (h *ActionHandler) GetUsers(ctx context.Context, req *GetUsersRequest) ([]*types.User, error) {
if !h.IsUserAdmin(ctx) {
return nil, errors.Errorf("user not logged in")
}
users, resp, err := h.configstoreClient.GetUsers(ctx, req.Start, req.Limit, req.Asc) users, resp, err := h.configstoreClient.GetUsers(ctx, req.Start, req.Limit, req.Asc)
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, err) return nil, ErrFromRemote(resp, err)
@ -67,6 +75,10 @@ type CreateUserRequest struct {
} }
func (h *ActionHandler) CreateUser(ctx context.Context, req *CreateUserRequest) (*types.User, error) { func (h *ActionHandler) CreateUser(ctx context.Context, req *CreateUserRequest) (*types.User, error) {
if !h.IsUserAdmin(ctx) {
return nil, errors.Errorf("user not admin")
}
if req.UserName == "" { if req.UserName == "" {
return nil, util.NewErrBadRequest(errors.Errorf("user name required")) return nil, util.NewErrBadRequest(errors.Errorf("user name required"))
} }
@ -490,10 +502,20 @@ func (h *ActionHandler) HandleRemoteSourceAuth(ctx context.Context, remoteSource
switch requestType { switch requestType {
case RemoteSourceRequestTypeCreateUserLA: case RemoteSourceRequestTypeCreateUserLA:
req := req.(*CreateUserLARequest) req := req.(*CreateUserLARequest)
user, resp, err := h.configstoreClient.GetUser(ctx, req.UserRef) user, resp, err := h.configstoreClient.GetUser(ctx, req.UserRef)
if err != nil { if err != nil {
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", req.UserRef)) return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", req.UserRef))
} }
curUserID := h.CurrentUserID(ctx)
// user must be already logged in the create a linked account and can create a
// linked account only on itself.
if user.ID != curUserID {
return nil, util.NewErrBadRequest(errors.Errorf("logged in user cannot create linked account for another user"))
}
var la *types.LinkedAccount var la *types.LinkedAccount
for _, v := range user.LinkedAccounts { for _, v := range user.LinkedAccounts {
if v.RemoteSourceID == rs.ID { if v.RemoteSourceID == rs.ID {
@ -729,6 +751,10 @@ func (h *ActionHandler) HandleOauth2Callback(ctx context.Context, code, state st
} }
func (h *ActionHandler) DeleteUser(ctx context.Context, userRef string) error { func (h *ActionHandler) DeleteUser(ctx context.Context, userRef string) error {
if !h.IsUserAdmin(ctx) {
return errors.Errorf("user not logged in")
}
resp, err := h.configstoreClient.DeleteUser(ctx, userRef) resp, err := h.configstoreClient.DeleteUser(ctx, userRef)
if err != nil { if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete user")) return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete user"))
@ -737,7 +763,24 @@ func (h *ActionHandler) DeleteUser(ctx context.Context, userRef string) error {
} }
func (h *ActionHandler) DeleteUserLA(ctx context.Context, userRef, laID string) error { func (h *ActionHandler) DeleteUserLA(ctx context.Context, userRef, laID string) error {
resp, err := h.configstoreClient.DeleteUserLA(ctx, userRef, laID) if !h.IsUserLoggedOrAdmin(ctx) {
return errors.Errorf("user not logged in")
}
isAdmin := !h.IsUserAdmin(ctx)
curUserID := h.CurrentUserID(ctx)
user, resp, err := h.configstoreClient.GetUser(ctx, userRef)
if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", userRef))
}
// only admin or the same logged user can create a token
if !isAdmin && user.ID != curUserID {
return util.NewErrBadRequest(errors.Errorf("logged in user cannot create token for another user"))
}
resp, err = h.configstoreClient.DeleteUserLA(ctx, userRef, laID)
if err != nil { if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete user linked account")) return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete user linked account"))
} }
@ -745,7 +788,24 @@ func (h *ActionHandler) DeleteUserLA(ctx context.Context, userRef, laID string)
} }
func (h *ActionHandler) DeleteUserToken(ctx context.Context, userRef, tokenName string) error { func (h *ActionHandler) DeleteUserToken(ctx context.Context, userRef, tokenName string) error {
resp, err := h.configstoreClient.DeleteUserToken(ctx, userRef, tokenName) if !h.IsUserLoggedOrAdmin(ctx) {
return errors.Errorf("user not logged in")
}
isAdmin := !h.IsUserAdmin(ctx)
curUserID := h.CurrentUserID(ctx)
user, resp, err := h.configstoreClient.GetUser(ctx, userRef)
if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", userRef))
}
// only admin or the same logged user can create a token
if !isAdmin && user.ID != curUserID {
return util.NewErrBadRequest(errors.Errorf("logged in user cannot delete token for another user"))
}
resp, err = h.configstoreClient.DeleteUserToken(ctx, userRef, tokenName)
if err != nil { if err != nil {
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete user token")) return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete user token"))
} }

View File

@ -80,6 +80,14 @@ type CreateVariableRequest struct {
} }
func (h *ActionHandler) CreateVariable(ctx context.Context, req *CreateVariableRequest) (*csapi.Variable, []*csapi.Secret, error) { func (h *ActionHandler) CreateVariable(ctx context.Context, req *CreateVariableRequest) (*csapi.Variable, []*csapi.Secret, error) {
isVariableOwner, err := h.IsVariableOwner(ctx, req.ParentType, req.ParentRef)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to determine ownership")
}
if !isVariableOwner {
return nil, nil, util.NewErrForbidden(errors.Errorf("user not authorized"))
}
if !util.ValidateName(req.Name) { if !util.ValidateName(req.Name) {
return nil, nil, util.NewErrBadRequest(errors.Errorf("invalid variable name %q", req.Name)) return nil, nil, util.NewErrBadRequest(errors.Errorf("invalid variable name %q", req.Name))
} }
@ -134,7 +142,14 @@ func (h *ActionHandler) CreateVariable(ctx context.Context, req *CreateVariableR
} }
func (h *ActionHandler) DeleteVariable(ctx context.Context, parentType types.ConfigType, parentRef, name string) error { func (h *ActionHandler) DeleteVariable(ctx context.Context, parentType types.ConfigType, parentRef, name string) error {
var err error isVariableOwner, err := h.IsVariableOwner(ctx, parentType, parentRef)
if err != nil {
return errors.Wrapf(err, "failed to determine ownership")
}
if !isVariableOwner {
return util.NewErrForbidden(errors.Errorf("user not authorized"))
}
var resp *http.Response var resp *http.Response
switch parentType { switch parentType {
case types.ConfigTypeProjectGroup: case types.ConfigTypeProjectGroup:

View File

@ -313,9 +313,10 @@ func (h *RunsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query() q := r.URL.Query()
groups := q["group"] // we currently accept only one group
group := q.Get("group")
// we require that groups are specified to not return all runs // we require that groups are specified to not return all runs
if len(groups) == 0 { if group == "" {
httpError(w, util.NewErrBadRequest(errors.Errorf("no groups specified"))) httpError(w, util.NewErrBadRequest(errors.Errorf("no groups specified")))
return return
} }
@ -350,7 +351,7 @@ func (h *RunsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
areq := &action.GetRunsRequest{ areq := &action.GetRunsRequest{
PhaseFilter: phaseFilter, PhaseFilter: phaseFilter,
Groups: groups, Group: group,
LastRun: lastRun, LastRun: lastRun,
ChangeGroups: changeGroups, ChangeGroups: changeGroups,
StartRunID: start, StartRunID: start,

View File

@ -27,8 +27,11 @@ import (
type GroupType string type GroupType string
const ( const (
GroupTypeProject GroupType = "project" // base groups
GroupTypeUser GroupType = "user" GroupTypeProject GroupType = "project"
GroupTypeUser GroupType = "user"
// sub groups
GroupTypeBranch GroupType = "branch" GroupTypeBranch GroupType = "branch"
GroupTypeTag GroupType = "tag" GroupTypeTag GroupType = "tag"
GroupTypePullRequest GroupType = "pr" GroupTypePullRequest GroupType = "pr"
@ -49,10 +52,10 @@ func GenRunGroup(baseGroupType GroupType, baseGroupID string, webhookData *types
panic(fmt.Errorf("invalid webhook event type: %q", webhookData.Event)) panic(fmt.Errorf("invalid webhook event type: %q", webhookData.Event))
} }
func ProjectIDFromRunGroup(group string) (string, error) { func GroupTypeIDFromRunGroup(group string) (GroupType, string, error) {
pl := util.PathList(group) pl := util.PathList(group)
if len(pl) < 2 { if len(pl) < 2 {
return "", errors.Errorf("cannot determine group project id, wrong group path %q", group) return "", "", errors.Errorf("cannot determine group project id, wrong group path %q", group)
} }
return pl[1], nil return GroupType(pl[0]), pl[1], nil
} }