diff --git a/internal/services/gateway/action/auth.go b/internal/services/gateway/action/auth.go index 6c7ed9c7c..84f4608af 100644 --- a/internal/services/gateway/action/auth.go +++ b/internal/services/gateway/action/auth.go @@ -48,131 +48,143 @@ 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 - } +func (h *ActionHandler) userActionsForUser(ctx context.Context, userRef string) ([]cstypes.ActionType, error) { + actions := []cstypes.ActionType{} userID := h.CurrentUserID(ctx) if userID == "" { - return false, nil + return actions, nil + } + + // By default any logged user can get an user + actions = append(actions, cstypes.ActionTypeGetUser) + + // no existing user + if userRef == "" { + return actions, nil } - userOrgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userID) + user, resp, err := h.configstoreClient.GetUser(ctx, userRef) if err != nil { - return false, errors.Errorf("failed to get user orgs: %w", ErrFromRemote(resp, err)) + return nil, ErrFromRemote(resp, err) } - for _, userOrg := range userOrgs { - if userOrg.Organization.ID != orgID { - continue - } - if userOrg.Role == cstypes.OrgMemberRoleOwner { - return true, nil - } + ownerActions, err := h.userActionsForOwner(ctx, cstypes.ConfigTypeUser, user.ID) + if err != nil { + return nil, err } + actions = append(actions, ownerActions...) - return false, nil + return actions, nil } -func (h *ActionHandler) IsProjectOwner(ctx context.Context, ownerType cstypes.ConfigType, ownerID string) (bool, error) { - isAdmin := h.IsUserAdmin(ctx) - if isAdmin { - return true, nil - } +func (h *ActionHandler) userActionsForOrg(ctx context.Context, orgRef string) ([]cstypes.ActionType, error) { + actions := []cstypes.ActionType{} userID := h.CurrentUserID(ctx) if userID == "" { - return false, nil + return actions, nil } - if ownerType == cstypes.ConfigTypeUser { - if userID == ownerID { - return true, nil - } + // By default any logged user can create an org + actions = append(actions, cstypes.ActionTypeCreateOrg) + + // no existing org + if orgRef == "" { + return actions, nil } - if ownerType == cstypes.ConfigTypeOrg { - userOrgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userID) - if err != nil { - return false, errors.Errorf("failed to get user orgs: %w", ErrFromRemote(resp, err)) - } + org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef) + if err != nil { + return nil, ErrFromRemote(resp, err) + } - for _, userOrg := range userOrgs { - if userOrg.Organization.ID != ownerID { - continue - } - if userOrg.Role == cstypes.OrgMemberRoleOwner { - return true, nil - } - } + if org.Visibility == cstypes.VisibilityPublic { + actions = append(actions, cstypes.OrgMemberActions...) } - return false, nil + ownerRoles, err := h.userActionsForOwner(ctx, cstypes.ConfigTypeOrg, org.ID) + if err != nil { + return nil, err + } + actions = append(actions, ownerRoles...) + + return actions, nil } -func (h *ActionHandler) IsProjectMember(ctx context.Context, ownerType cstypes.ConfigType, ownerID string) (bool, error) { - isAdmin := h.IsUserAdmin(ctx) - if isAdmin { - return true, nil - } +func (h *ActionHandler) userActionsForProjectGroup(ctx context.Context, projectGroupRef string) ([]cstypes.ActionType, error) { + actions := []cstypes.ActionType{} userID := h.CurrentUserID(ctx) if userID == "" { - return false, nil + return actions, nil } - if ownerType == cstypes.ConfigTypeUser { - if userID == ownerID { - return true, nil - } + p, resp, err := h.configstoreClient.GetProjectGroup(ctx, projectGroupRef) + if err != nil { + return nil, ErrFromRemote(resp, err) } - if ownerType == cstypes.ConfigTypeOrg { - userOrgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userID) - if err != nil { - return false, errors.Errorf("failed to get user orgs: %w", ErrFromRemote(resp, err)) - } + if p.GlobalVisibility == cstypes.VisibilityPublic { + actions = append(actions, cstypes.ProjectReadActions...) + } - for _, userOrg := range userOrgs { - if userOrg.Organization.ID != ownerID { - continue - } - return true, nil - } + ownerRoles, err := h.userActionsForOwner(ctx, p.OwnerType, p.OwnerID) + if err != nil { + return nil, err } + actions = append(actions, ownerRoles...) - return false, nil + return actions, nil } -func (h *ActionHandler) IsVariableOwner(ctx context.Context, parentType cstypes.ConfigType, parentRef string) (bool, error) { - var ownerType cstypes.ConfigType - var ownerID string +func (h *ActionHandler) userActionsForProject(ctx context.Context, projectRef string) ([]cstypes.ActionType, error) { + actions := []cstypes.ActionType{} + + userID := h.CurrentUserID(ctx) + if userID == "" { + return actions, nil + } + + p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) + if err != nil { + return nil, ErrFromRemote(resp, err) + } + + if p.GlobalVisibility == cstypes.VisibilityPublic { + actions = append(actions, cstypes.ProjectReadActions...) + } + ownerRoles, err := h.userActionsForOwner(ctx, p.OwnerType, p.OwnerID) + if err != nil { + return nil, err + } + actions = append(actions, ownerRoles...) + + return actions, nil +} + +func (h *ActionHandler) userActionsForVariable(ctx context.Context, parentType cstypes.ConfigType, parentRef string) ([]cstypes.ActionType, error) { switch parentType { case cstypes.ConfigTypeProjectGroup: - pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, parentRef) - if err != nil { - return false, errors.Errorf("failed to get project group %q: %w", parentRef, ErrFromRemote(resp, err)) - } - ownerType = pg.OwnerType - ownerID = pg.OwnerID + return h.userActionsForProjectGroup(ctx, parentRef) case cstypes.ConfigTypeProject: - p, resp, err := h.configstoreClient.GetProject(ctx, parentRef) - if err != nil { - return false, errors.Errorf("failed to get project %q: %w", parentRef, ErrFromRemote(resp, err)) - } - ownerType = p.OwnerType - ownerID = p.OwnerID + return h.userActionsForProject(ctx, parentRef) + default: + return nil, errors.Errorf("wrong parent type: %q", parentType) } - - return h.IsProjectOwner(ctx, ownerType, ownerID) } -func (h *ActionHandler) CanGetRun(ctx context.Context, runGroup string) (bool, error) { +func (h *ActionHandler) userActionsForRun(ctx context.Context, runGroup string) ([]cstypes.ActionType, error) { + actions := []cstypes.ActionType{} + + userID := h.CurrentUserID(ctx) + if userID == "" { + return actions, nil + } + groupType, groupID, err := common.GroupTypeIDFromRunGroup(runGroup) if err != nil { - return false, err + return nil, err } var visibility cstypes.Visibility @@ -182,59 +194,137 @@ func (h *ActionHandler) CanGetRun(ctx context.Context, runGroup string) (bool, e case common.GroupTypeProject: p, resp, err := h.configstoreClient.GetProject(ctx, groupID) if err != nil { - return false, ErrFromRemote(resp, err) + return nil, ErrFromRemote(resp, err) } ownerType = p.OwnerType ownerID = p.OwnerID visibility = p.GlobalVisibility + case common.GroupTypeUser: // user direct runs ownerType = cstypes.ConfigTypeUser ownerID = groupID visibility = cstypes.VisibilityPrivate + default: + return nil, errors.Errorf("wrong run group type: %q", runGroup) } if visibility == cstypes.VisibilityPublic { - return true, nil + actions = append(actions, cstypes.ProjectReadActions...) } - isProjectMember, err := h.IsProjectMember(ctx, ownerType, ownerID) + ownerRoles, err := h.userActionsForOwner(ctx, ownerType, ownerID) if err != nil { - return false, errors.Errorf("failed to determine ownership: %w", err) + return nil, err + } + actions = append(actions, ownerRoles...) + + return actions, nil +} + +func (h *ActionHandler) userActionsForOwner(ctx context.Context, ownerType cstypes.ConfigType, ownerID string) ([]cstypes.ActionType, error) { + actions := []cstypes.ActionType{} + + userID := h.CurrentUserID(ctx) + if userID == "" { + return actions, nil } - if !isProjectMember { - return false, nil + + switch ownerType { + case cstypes.ConfigTypeUser: + if userID == ownerID { + actions = append(actions, cstypes.UserOwnerActions...) + } + case cstypes.ConfigTypeOrg: + userOrgs, resp, err := h.configstoreClient.GetUserOrgs(ctx, userID) + if err != nil { + return nil, errors.Errorf("failed to get user orgs: %w", ErrFromRemote(resp, err)) + } + + for _, userOrg := range userOrgs { + if userOrg.Organization.ID != ownerID { + continue + } + if userOrg.Role == cstypes.OrgMemberRoleOwner { + actions = append(actions, cstypes.OrgOwnerActions...) + } + if userOrg.Role == cstypes.OrgMemberRoleMember { + actions = append(actions, cstypes.OrgMemberActions...) + } + } } - return true, nil + + return actions, nil } -func (h *ActionHandler) CanDoRunActions(ctx context.Context, runGroup string) (bool, error) { - groupType, groupID, err := common.GroupTypeIDFromRunGroup(runGroup) +func (h *ActionHandler) CanDoUserAction(ctx context.Context, action cstypes.ActionType, userRef string) (bool, error) { + actions, err := h.userActionsForUser(ctx, userRef) if err != nil { return false, err } - var ownerType cstypes.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 direct runs - ownerType = cstypes.ConfigTypeUser - ownerID = groupID + return h.CanDoAction(ctx, actions, action) +} + +func (h *ActionHandler) CanDoOrgAction(ctx context.Context, action cstypes.ActionType, orgRef string) (bool, error) { + actions, err := h.userActionsForOrg(ctx, orgRef) + if err != nil { + return false, err + } + + return h.CanDoAction(ctx, actions, action) +} + +func (h *ActionHandler) CanDoProjectGroupAction(ctx context.Context, action cstypes.ActionType, projectGroupRef string) (bool, error) { + actions, err := h.userActionsForProjectGroup(ctx, projectGroupRef) + if err != nil { + return false, err } - isProjectOwner, err := h.IsProjectOwner(ctx, ownerType, ownerID) + return h.CanDoAction(ctx, actions, action) +} + +func (h *ActionHandler) CanDoProjectAction(ctx context.Context, action cstypes.ActionType, projectRef string) (bool, error) { + actions, err := h.userActionsForProject(ctx, projectRef) if err != nil { - return false, errors.Errorf("failed to determine ownership: %w", err) + return false, err } - if !isProjectOwner { - return false, nil + + return h.CanDoAction(ctx, actions, action) +} + +func (h *ActionHandler) CanDoVariableAction(ctx context.Context, action cstypes.ActionType, parentType cstypes.ConfigType, parentRef string) (bool, error) { + actions, err := h.userActionsForVariable(ctx, parentType, parentRef) + if err != nil { + return false, err } - return true, nil + + return h.CanDoAction(ctx, actions, action) +} +func (h *ActionHandler) CanDoSecretAction(ctx context.Context, action cstypes.ActionType, parentType cstypes.ConfigType, parentRef string) (bool, error) { + return h.CanDoVariableAction(ctx, action, parentType, parentRef) +} + +func (h *ActionHandler) CanDoRunAction(ctx context.Context, action cstypes.ActionType, runGroup string) (bool, error) { + actions, err := h.userActionsForRun(ctx, runGroup) + if err != nil { + return false, err + } + + return h.CanDoAction(ctx, actions, action) +} + +func (h *ActionHandler) CanDoAction(ctx context.Context, actions []cstypes.ActionType, action cstypes.ActionType) (bool, error) { + isAdmin := h.IsUserAdmin(ctx) + if isAdmin { + actions = append(actions, cstypes.AdminActions...) + } + + for _, a := range actions { + if a != action { + continue + } + return true, nil + } + + return false, nil } diff --git a/internal/services/gateway/action/org.go b/internal/services/gateway/action/org.go index cd64cc0ff..a5234ea41 100644 --- a/internal/services/gateway/action/org.go +++ b/internal/services/gateway/action/org.go @@ -24,6 +24,14 @@ import ( ) func (h *ActionHandler) GetOrg(ctx context.Context, orgRef string) (*cstypes.Organization, error) { + authorized, err := h.CanDoOrgAction(ctx, cstypes.ActionTypeGetOrg, "") + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef) if err != nil { return nil, ErrFromRemote(resp, err) @@ -38,45 +46,19 @@ type GetOrgsRequest struct { } func (h *ActionHandler) GetOrgs(ctx context.Context, req *GetOrgsRequest) ([]*cstypes.Organization, error) { - orgs, resp, err := h.configstoreClient.GetOrgs(ctx, req.Start, req.Limit, req.Asc) + authorized, err := h.CanDoOrgAction(ctx, cstypes.ActionTypeGetOrg, "") if err != nil { - return nil, ErrFromRemote(resp, err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - return orgs, nil -} - -type OrgMembersResponse struct { - Organization *cstypes.Organization - Members []*OrgMemberResponse -} - -type OrgMemberResponse struct { - User *cstypes.User - Role cstypes.OrgMemberRole -} - -func (h *ActionHandler) GetOrgMembers(ctx context.Context, orgRef string) (*OrgMembersResponse, error) { - org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef) - if err != nil { - return nil, ErrFromRemote(resp, err) + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } - orgMembers, resp, err := h.configstoreClient.GetOrgMembers(ctx, orgRef) + orgs, resp, err := h.configstoreClient.GetOrgs(ctx, req.Start, req.Limit, req.Asc) if err != nil { return nil, ErrFromRemote(resp, err) } - - res := &OrgMembersResponse{ - Organization: org, - Members: make([]*OrgMemberResponse, len(orgMembers)), - } - for i, orgMember := range orgMembers { - res.Members[i] = &OrgMemberResponse{ - User: orgMember.User, - Role: orgMember.Role, - } - } - return res, nil + return orgs, nil } type CreateOrgRequest struct { @@ -87,8 +69,12 @@ type CreateOrgRequest struct { } func (h *ActionHandler) CreateOrg(ctx context.Context, req *CreateOrgRequest) (*cstypes.Organization, error) { - if !h.IsUserLoggedOrAdmin(ctx) { - return nil, errors.Errorf("user not logged in") + authorized, err := h.CanDoOrgAction(ctx, cstypes.ActionTypeCreateOrg, "") + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } if req.Name == "" { @@ -117,26 +103,63 @@ func (h *ActionHandler) CreateOrg(ctx context.Context, req *CreateOrgRequest) (* } func (h *ActionHandler) DeleteOrg(ctx context.Context, orgRef string) error { - org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef) + authorized, err := h.CanDoOrgAction(ctx, cstypes.ActionTypeDeleteOrg, orgRef) if err != nil { - return ErrFromRemote(resp, err) + return errors.Errorf("failed to determine authorization: %w", err) } - - isOrgOwner, err := h.IsOrgOwner(ctx, org.ID) - if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) - } - if !isOrgOwner { + if !authorized { return util.NewErrForbidden(errors.Errorf("user not authorized")) } - resp, err = h.configstoreClient.DeleteOrg(ctx, orgRef) + resp, err := h.configstoreClient.DeleteOrg(ctx, orgRef) if err != nil { return errors.Errorf("failed to delete org: %w", ErrFromRemote(resp, err)) } return nil } +type OrgMembersResponse struct { + Organization *cstypes.Organization + Members []*OrgMemberResponse +} + +type OrgMemberResponse struct { + User *cstypes.User + Role cstypes.OrgMemberRole +} + +func (h *ActionHandler) GetOrgMembers(ctx context.Context, orgRef string) (*OrgMembersResponse, error) { + authorized, err := h.CanDoOrgAction(ctx, cstypes.ActionTypeGetOrgMembers, orgRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + + org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef) + if err != nil { + return nil, ErrFromRemote(resp, err) + } + + orgMembers, resp, err := h.configstoreClient.GetOrgMembers(ctx, orgRef) + if err != nil { + return nil, ErrFromRemote(resp, err) + } + + res := &OrgMembersResponse{ + Organization: org, + Members: make([]*OrgMemberResponse, len(orgMembers)), + } + for i, orgMember := range orgMembers { + res.Members[i] = &OrgMemberResponse{ + User: orgMember.User, + Role: orgMember.Role, + } + } + return res, nil +} + type AddOrgMemberResponse struct { OrganizationMember *cstypes.OrganizationMember Org *cstypes.Organization @@ -144,6 +167,14 @@ type AddOrgMemberResponse struct { } func (h *ActionHandler) AddOrgMember(ctx context.Context, orgRef, userRef string, role cstypes.OrgMemberRole) (*AddOrgMemberResponse, error) { + authorized, err := h.CanDoOrgAction(ctx, cstypes.ActionTypeAddOrgMembers, orgRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef) if err != nil { return nil, ErrFromRemote(resp, err) @@ -153,14 +184,6 @@ func (h *ActionHandler) AddOrgMember(ctx context.Context, orgRef, userRef string return nil, ErrFromRemote(resp, err) } - isOrgOwner, err := h.IsOrgOwner(ctx, org.ID) - if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) - } - if !isOrgOwner { - return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) - } - orgmember, resp, err := h.configstoreClient.AddOrgMember(ctx, orgRef, userRef, role) if err != nil { return nil, errors.Errorf("failed to add/update organization member: %w", ErrFromRemote(resp, err)) @@ -174,20 +197,15 @@ func (h *ActionHandler) AddOrgMember(ctx context.Context, orgRef, userRef string } func (h *ActionHandler) RemoveOrgMember(ctx context.Context, orgRef, userRef string) error { - org, resp, err := h.configstoreClient.GetOrg(ctx, orgRef) - if err != nil { - return ErrFromRemote(resp, err) - } - - isOrgOwner, err := h.IsOrgOwner(ctx, org.ID) + authorized, err := h.CanDoOrgAction(ctx, cstypes.ActionTypeRemoveOrgMembers, orgRef) if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) + return errors.Errorf("failed to determine authorization: %w", err) } - if !isOrgOwner { + if !authorized { return util.NewErrForbidden(errors.Errorf("user not authorized")) } - resp, err = h.configstoreClient.RemoveOrgMember(ctx, orgRef, userRef) + resp, err := h.configstoreClient.RemoveOrgMember(ctx, orgRef, userRef) if err != nil { return errors.Errorf("failed to remove organization member: %w", ErrFromRemote(resp, err)) } diff --git a/internal/services/gateway/action/project.go b/internal/services/gateway/action/project.go index 5c035e84d..55d9305dd 100644 --- a/internal/services/gateway/action/project.go +++ b/internal/services/gateway/action/project.go @@ -31,20 +31,17 @@ import ( ) func (h *ActionHandler) GetProject(ctx context.Context, projectRef string) (*csapitypes.Project, error) { - project, resp, err := h.configstoreClient.GetProject(ctx, projectRef) + authorized, err := h.CanDoProjectAction(ctx, cstypes.ActionTypeGetProject, projectRef) if err != nil { - return nil, ErrFromRemote(resp, err) + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } - isProjectMember, err := h.IsProjectMember(ctx, project.OwnerType, project.OwnerID) + project, resp, err := h.configstoreClient.GetProject(ctx, projectRef) if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) - } - if project.GlobalVisibility == cstypes.VisibilityPublic { - return project, nil - } - if !isProjectMember { - return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + return nil, ErrFromRemote(resp, err) } return project, nil @@ -72,16 +69,11 @@ func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectReq parentRef = path.Join("user", user.Name) } - pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, parentRef) - if err != nil { - return nil, errors.Errorf("failed to get project group %q: %w", parentRef, ErrFromRemote(resp, err)) - } - - isProjectOwner, err := h.IsProjectOwner(ctx, pg.OwnerType, pg.OwnerID) + authorized, err := h.CanDoProjectGroupAction(ctx, cstypes.ActionTypeCreateProject, parentRef) if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - if !isProjectOwner { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -95,6 +87,11 @@ func (h *ActionHandler) CreateProject(ctx context.Context, req *CreateProjectReq return nil, util.NewErrBadRequest(errors.Errorf("empty remote repo path")) } + pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, parentRef) + if err != nil { + return nil, errors.Errorf("failed to get project group %q: %w", parentRef, ErrFromRemote(resp, err)) + } + projectPath := path.Join(pg.Path, req.Name) _, resp, err = h.configstoreClient.GetProject(ctx, projectPath) if err != nil { @@ -187,17 +184,17 @@ type UpdateProjectRequest struct { } func (h *ActionHandler) UpdateProject(ctx context.Context, projectRef string, req *UpdateProjectRequest) (*csapitypes.Project, error) { - p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) + authorized, err := h.CanDoProjectAction(ctx, cstypes.ActionTypeUpdateProject, projectRef) if err != nil { - return nil, errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } - isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID) + p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) - } - if !isProjectOwner { - return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + return nil, errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) } if req.Name != nil { @@ -228,17 +225,17 @@ func (h *ActionHandler) ProjectUpdateRepoLinkedAccount(ctx context.Context, proj return nil, errors.Errorf("failed to get user %q: %w", curUserID, ErrFromRemote(resp, err)) } - p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) + authorized, err := h.CanDoProjectAction(ctx, cstypes.ActionTypeUpdateProject, projectRef) if err != nil { - return nil, errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } - isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID) + p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) - } - if !isProjectOwner { - return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + return nil, errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) } rs, resp, err := h.configstoreClient.GetRemoteSource(ctx, p.RemoteSourceID) @@ -357,17 +354,17 @@ func (h *ActionHandler) genWebhookURL(project *csapitypes.Project) (string, erro } func (h *ActionHandler) ReconfigProject(ctx context.Context, projectRef string) error { - p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) + authorized, err := h.CanDoProjectAction(ctx, cstypes.ActionTypeUpdateProject, projectRef) if err != nil { - return errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) + return errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) } - isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID) + p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) - } - if !isProjectOwner { - return util.NewErrForbidden(errors.Errorf("user not authorized")) + return errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) } user, rs, la, err := h.getRemoteRepoAccessData(ctx, p.LinkedAccountID) @@ -381,17 +378,17 @@ func (h *ActionHandler) ReconfigProject(ctx context.Context, projectRef string) } func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) error { - p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) + authorized, err := h.CanDoProjectAction(ctx, cstypes.ActionTypeDeleteProject, projectRef) if err != nil { - return errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) + return errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) } - isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID) + p, resp, err := h.configstoreClient.GetProject(ctx, projectRef) if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) - } - if !isProjectOwner { - return util.NewErrForbidden(errors.Errorf("user not authorized")) + return errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) } // get data needed for repo cleanup @@ -422,6 +419,14 @@ func (h *ActionHandler) DeleteProject(ctx context.Context, projectRef string) er } func (h *ActionHandler) ProjectCreateRun(ctx context.Context, projectRef, branch, tag, refName, commitSHA string) error { + authorized, err := h.CanDoProjectAction(ctx, cstypes.ActionTypeCreateRun, projectRef) + if err != nil { + return errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) + } + curUserID := h.CurrentUserID(ctx) user, resp, err := h.configstoreClient.GetUser(ctx, curUserID) @@ -434,14 +439,6 @@ func (h *ActionHandler) ProjectCreateRun(ctx context.Context, projectRef, branch return errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) } - isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID) - if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) - } - if !isProjectOwner { - return util.NewErrForbidden(errors.Errorf("user not authorized")) - } - rs, resp, err := h.configstoreClient.GetRemoteSource(ctx, p.RemoteSourceID) if err != nil { return errors.Errorf("failed to get remote source %q: %w", p.RemoteSourceID, ErrFromRemote(resp, err)) diff --git a/internal/services/gateway/action/projectgroup.go b/internal/services/gateway/action/projectgroup.go index 350b9f688..242acd8fd 100644 --- a/internal/services/gateway/action/projectgroup.go +++ b/internal/services/gateway/action/projectgroup.go @@ -26,6 +26,14 @@ import ( ) func (h *ActionHandler) GetProjectGroup(ctx context.Context, projectGroupRef string) (*csapitypes.ProjectGroup, error) { + authorized, err := h.CanDoProjectGroupAction(ctx, cstypes.ActionTypeGetProjectGroup, projectGroupRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + projectGroup, resp, err := h.configstoreClient.GetProjectGroup(ctx, projectGroupRef) if err != nil { return nil, ErrFromRemote(resp, err) @@ -34,6 +42,14 @@ func (h *ActionHandler) GetProjectGroup(ctx context.Context, projectGroupRef str } func (h *ActionHandler) GetProjectGroupSubgroups(ctx context.Context, projectGroupRef string) ([]*csapitypes.ProjectGroup, error) { + authorized, err := h.CanDoProjectGroupAction(ctx, cstypes.ActionTypeGetProjectGroup, projectGroupRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + projectGroups, resp, err := h.configstoreClient.GetProjectGroupSubgroups(ctx, projectGroupRef) if err != nil { return nil, ErrFromRemote(resp, err) @@ -42,6 +58,14 @@ func (h *ActionHandler) GetProjectGroupSubgroups(ctx context.Context, projectGro } func (h *ActionHandler) GetProjectGroupProjects(ctx context.Context, projectGroupRef string) ([]*csapitypes.Project, error) { + authorized, err := h.CanDoProjectGroupAction(ctx, cstypes.ActionTypeGetProjectGroup, projectGroupRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + projects, resp, err := h.configstoreClient.GetProjectGroupProjects(ctx, projectGroupRef) if err != nil { return nil, ErrFromRemote(resp, err) @@ -57,23 +81,18 @@ type CreateProjectGroupRequest struct { } func (h *ActionHandler) CreateProjectGroup(ctx context.Context, req *CreateProjectGroupRequest) (*csapitypes.ProjectGroup, error) { - if !util.ValidateName(req.Name) { - return nil, util.NewErrBadRequest(errors.Errorf("invalid projectGroup name %q", req.Name)) - } - - pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, req.ParentRef) + authorized, err := h.CanDoProjectGroupAction(ctx, cstypes.ActionTypeCreateProjectGroup, req.ParentRef) if err != nil { - return nil, errors.Errorf("failed to get project group %q: %w", req.ParentRef, ErrFromRemote(resp, err)) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - - isProjectOwner, err := h.IsProjectOwner(ctx, pg.OwnerType, pg.OwnerID) - if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) - } - if !isProjectOwner { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } + if !util.ValidateName(req.Name) { + return nil, util.NewErrBadRequest(errors.Errorf("invalid projectGroup name %q", req.Name)) + } + user, resp, err := h.configstoreClient.GetUser(ctx, req.CurrentUserID) if err != nil { return nil, errors.Errorf("failed to get user %q: %w", req.CurrentUserID, ErrFromRemote(resp, err)) @@ -112,17 +131,17 @@ type UpdateProjectGroupRequest struct { } func (h *ActionHandler) UpdateProjectGroup(ctx context.Context, projectGroupRef string, req *UpdateProjectGroupRequest) (*csapitypes.ProjectGroup, error) { - pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, projectGroupRef) + authorized, err := h.CanDoProjectGroupAction(ctx, cstypes.ActionTypeUpdateProjectGroup, projectGroupRef) if err != nil { - return nil, errors.Errorf("failed to get project group %q: %w", projectGroupRef, ErrFromRemote(resp, err)) + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } - isProjectOwner, err := h.IsProjectOwner(ctx, pg.OwnerType, pg.OwnerID) + pg, resp, err := h.configstoreClient.GetProjectGroup(ctx, projectGroupRef) if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) - } - if !isProjectOwner { - return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + return nil, errors.Errorf("failed to get project group %q: %w", projectGroupRef, ErrFromRemote(resp, err)) } if req.Name != nil { @@ -145,21 +164,16 @@ func (h *ActionHandler) UpdateProjectGroup(ctx context.Context, projectGroupRef return rp, nil } -func (h *ActionHandler) DeleteProjectGroup(ctx context.Context, projectRef string) error { - p, resp, err := h.configstoreClient.GetProjectGroup(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to get project %q: %w", projectRef, ErrFromRemote(resp, err)) - } - - isProjectOwner, err := h.IsProjectOwner(ctx, p.OwnerType, p.OwnerID) +func (h *ActionHandler) DeleteProjectGroup(ctx context.Context, projectGroupRef string) error { + authorized, err := h.CanDoProjectGroupAction(ctx, cstypes.ActionTypeDeleteProjectGroup, projectGroupRef) if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) + return errors.Errorf("failed to determine authorization: %w", err) } - if !isProjectOwner { + if !authorized { return util.NewErrForbidden(errors.Errorf("user not authorized")) } - resp, err = h.configstoreClient.DeleteProjectGroup(ctx, projectRef) + resp, err := h.configstoreClient.DeleteProjectGroup(ctx, projectGroupRef) if err != nil { return ErrFromRemote(resp, err) } diff --git a/internal/services/gateway/action/remotesource.go b/internal/services/gateway/action/remotesource.go index b3239aab5..1ae93659a 100644 --- a/internal/services/gateway/action/remotesource.go +++ b/internal/services/gateway/action/remotesource.go @@ -61,7 +61,7 @@ type CreateRemoteSourceRequest struct { func (h *ActionHandler) CreateRemoteSource(ctx context.Context, req *CreateRemoteSourceRequest) (*cstypes.RemoteSource, error) { if !h.IsUserAdmin(ctx) { - return nil, errors.Errorf("user not admin") + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } if !util.ValidateName(req.Name) { @@ -135,7 +135,7 @@ type UpdateRemoteSourceRequest struct { func (h *ActionHandler) UpdateRemoteSource(ctx context.Context, req *UpdateRemoteSourceRequest) (*cstypes.RemoteSource, error) { if !h.IsUserAdmin(ctx) { - return nil, errors.Errorf("user not admin") + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } rs, resp, err := h.configstoreClient.GetRemoteSource(ctx, req.RemoteSourceRef) @@ -183,7 +183,7 @@ func (h *ActionHandler) UpdateRemoteSource(ctx context.Context, req *UpdateRemot func (h *ActionHandler) DeleteRemoteSource(ctx context.Context, rsRef string) error { if !h.IsUserAdmin(ctx) { - return errors.Errorf("user not admin") + return util.NewErrForbidden(errors.Errorf("user not authorized")) } resp, err := h.configstoreClient.DeleteRemoteSource(ctx, rsRef) diff --git a/internal/services/gateway/action/run.go b/internal/services/gateway/action/run.go index ec7d662af..d8bc47de1 100644 --- a/internal/services/gateway/action/run.go +++ b/internal/services/gateway/action/run.go @@ -76,11 +76,11 @@ func (h *ActionHandler) GetRun(ctx context.Context, runID string) (*rsapitypes.R if err != nil { return nil, ErrFromRemote(resp, err) } - canGetRun, err := h.CanGetRun(ctx, runResp.RunConfig.Group) + authorized, err := h.CanDoRunAction(ctx, cstypes.ActionTypeGetRun, runResp.RunConfig.Group) if err != nil { - return nil, errors.Errorf("failed to determine permissions: %w", err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - if !canGetRun { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -99,11 +99,11 @@ type GetRunsRequest struct { } func (h *ActionHandler) GetRuns(ctx context.Context, req *GetRunsRequest) (*rsapitypes.GetRunsResponse, error) { - canGetRun, err := h.CanGetRun(ctx, req.Group) + authorized, err := h.CanDoRunAction(ctx, cstypes.ActionTypeGetRun, req.Group) if err != nil { - return nil, errors.Errorf("failed to determine permissions: %w", err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - if !canGetRun { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -129,11 +129,12 @@ func (h *ActionHandler) GetLogs(ctx context.Context, req *GetLogsRequest) (*http if err != nil { return nil, ErrFromRemote(resp, err) } - canGetRun, err := h.CanGetRun(ctx, runResp.RunConfig.Group) + + authorized, err := h.CanDoRunAction(ctx, cstypes.ActionTypeGetLog, runResp.RunConfig.Group) if err != nil { - return nil, errors.Errorf("failed to determine permissions: %w", err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - if !canGetRun { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -157,11 +158,12 @@ func (h *ActionHandler) DeleteLogs(ctx context.Context, req *DeleteLogsRequest) if err != nil { return ErrFromRemote(resp, err) } - canDoRunActions, err := h.CanDoRunActions(ctx, runResp.RunConfig.Group) + + authorized, err := h.CanDoRunAction(ctx, cstypes.ActionTypeDeleteLog, runResp.RunConfig.Group) if err != nil { - return errors.Errorf("failed to determine permissions: %w", err) + return errors.Errorf("failed to determine authorization: %w", err) } - if !canDoRunActions { + if !authorized { return util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -194,11 +196,24 @@ func (h *ActionHandler) RunAction(ctx context.Context, req *RunActionsRequest) ( if err != nil { return nil, ErrFromRemote(resp, err) } - canGetRun, err := h.CanDoRunActions(ctx, runResp.RunConfig.Group) + + var action cstypes.ActionType + switch req.ActionType { + case RunActionTypeRestart: + action = cstypes.ActionTypeRestartRun + case RunActionTypeCancel: + action = cstypes.ActionTypeCancelRun + case RunActionTypeStop: + action = cstypes.ActionTypeStopRun + default: + return nil, util.NewErrBadRequest(errors.Errorf("wrong run action type %q", req.ActionType)) + } + + authorized, err := h.CanDoRunAction(ctx, action, runResp.RunConfig.Group) if err != nil { - return nil, errors.Errorf("failed to determine permissions: %w", err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - if !canGetRun { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -260,13 +275,23 @@ func (h *ActionHandler) RunTaskAction(ctx context.Context, req *RunTaskActionsRe if err != nil { return ErrFromRemote(resp, err) } - canDoRunAction, err := h.CanDoRunActions(ctx, runResp.RunConfig.Group) + + var action cstypes.ActionType + switch req.ActionType { + case RunTaskActionTypeApprove: + action = cstypes.ActionTypeApproveTask + default: + return util.NewErrBadRequest(errors.Errorf("wrong run task action type %q", req.ActionType)) + } + + authorized, err := h.CanDoRunAction(ctx, action, runResp.RunConfig.Group) if err != nil { - return errors.Errorf("failed to determine permissions: %w", err) + return errors.Errorf("failed to determine authorization: %w", err) } - if !canDoRunAction { + if !authorized { return util.NewErrForbidden(errors.Errorf("user not authorized")) } + curUserID := h.CurrentUserID(ctx) if curUserID == "" { return util.NewErrBadRequest(errors.Errorf("no logged in user")) diff --git a/internal/services/gateway/action/secret.go b/internal/services/gateway/action/secret.go index 934257ec8..fb8e439a2 100644 --- a/internal/services/gateway/action/secret.go +++ b/internal/services/gateway/action/secret.go @@ -35,9 +35,16 @@ type GetSecretsRequest struct { } func (h *ActionHandler) GetSecrets(ctx context.Context, req *GetSecretsRequest) ([]*csapitypes.Secret, error) { + authorized, err := h.CanDoSecretAction(ctx, cstypes.ActionTypeGetSecret, req.ParentType, req.ParentRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + var cssecrets []*csapitypes.Secret var resp *http.Response - var err error switch req.ParentType { case cstypes.ConfigTypeProjectGroup: cssecrets, resp, err = h.configstoreClient.GetProjectGroupSecrets(ctx, req.ParentRef, req.Tree) @@ -73,11 +80,11 @@ type CreateSecretRequest struct { } func (h *ActionHandler) CreateSecret(ctx context.Context, req *CreateSecretRequest) (*csapitypes.Secret, error) { - isVariableOwner, err := h.IsVariableOwner(ctx, req.ParentType, req.ParentRef) + authorized, err := h.CanDoSecretAction(ctx, cstypes.ActionTypeCreateSecret, req.ParentType, req.ParentRef) if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - if !isVariableOwner { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -91,8 +98,8 @@ func (h *ActionHandler) CreateSecret(ctx context.Context, req *CreateSecretReque Data: req.Data, } - var resp *http.Response var rs *csapitypes.Secret + var resp *http.Response switch req.ParentType { case cstypes.ConfigTypeProjectGroup: h.log.Infof("creating project group secret") @@ -128,11 +135,11 @@ type UpdateSecretRequest struct { } func (h *ActionHandler) UpdateSecret(ctx context.Context, req *UpdateSecretRequest) (*csapitypes.Secret, error) { - isVariableOwner, err := h.IsVariableOwner(ctx, req.ParentType, req.ParentRef) + authorized, err := h.CanDoSecretAction(ctx, cstypes.ActionTypeUpdateSecret, req.ParentType, req.ParentRef) if err != nil { - return nil, errors.Errorf("failed to determine ownership: %w", err) + return nil, errors.Errorf("failed to determine authorization: %w", err) } - if !isVariableOwner { + if !authorized { return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -146,8 +153,8 @@ func (h *ActionHandler) UpdateSecret(ctx context.Context, req *UpdateSecretReque Data: req.Data, } - var resp *http.Response var rs *csapitypes.Secret + var resp *http.Response switch req.ParentType { case cstypes.ConfigTypeProjectGroup: h.log.Infof("updating project group secret") @@ -165,11 +172,11 @@ func (h *ActionHandler) UpdateSecret(ctx context.Context, req *UpdateSecretReque } func (h *ActionHandler) DeleteSecret(ctx context.Context, parentType cstypes.ConfigType, parentRef, name string) error { - isVariableOwner, err := h.IsVariableOwner(ctx, parentType, parentRef) + authorized, err := h.CanDoSecretAction(ctx, cstypes.ActionTypeDeleteSecret, parentType, parentRef) if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) + return errors.Errorf("failed to determine authorization: %w", err) } - if !isVariableOwner { + if !authorized { return util.NewErrForbidden(errors.Errorf("user not authorized")) } diff --git a/internal/services/gateway/action/user.go b/internal/services/gateway/action/user.go index 9fd88c74c..714889629 100644 --- a/internal/services/gateway/action/user.go +++ b/internal/services/gateway/action/user.go @@ -46,8 +46,12 @@ func isAccessTokenExpired(expiresAt time.Time) bool { } func (h *ActionHandler) GetUser(ctx context.Context, userRef string) (*cstypes.User, error) { - if !h.IsUserLoggedOrAdmin(ctx) { - return nil, errors.Errorf("user not logged in") + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeGetUser, userRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } user, resp, err := h.configstoreClient.GetUser(ctx, userRef) @@ -64,8 +68,12 @@ type GetUsersRequest struct { } func (h *ActionHandler) GetUsers(ctx context.Context, req *GetUsersRequest) ([]*cstypes.User, error) { - if !h.IsUserAdmin(ctx) { - return nil, errors.Errorf("user not logged in") + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeGetUser, "") + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } users, resp, err := h.configstoreClient.GetUsers(ctx, req.Start, req.Limit, req.Asc) @@ -80,8 +88,12 @@ type CreateUserRequest struct { } func (h *ActionHandler) CreateUser(ctx context.Context, req *CreateUserRequest) (*cstypes.User, error) { - if !h.IsUserAdmin(ctx) { - return nil, errors.Errorf("user not admin") + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeCreateUser, "") + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } if req.UserName == "" { @@ -111,16 +123,12 @@ type CreateUserTokenRequest struct { } func (h *ActionHandler) CreateUserToken(ctx context.Context, req *CreateUserTokenRequest) (string, error) { - var userID string - userIDVal := ctx.Value("userid") - if userIDVal != nil { - userID = userIDVal.(string) + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeUpdateUser, "") + if err != nil { + return "", errors.Errorf("failed to determine authorization: %w", err) } - - isAdmin := false - isAdminVal := ctx.Value("admin") - if isAdminVal != nil { - isAdmin = isAdminVal.(bool) + if !authorized { + return "", util.NewErrForbidden(errors.Errorf("user not authorized")) } userRef := req.UserRef @@ -129,10 +137,6 @@ func (h *ActionHandler) CreateUserToken(ctx context.Context, req *CreateUserToke return "", errors.Errorf("failed to get user: %w", ErrFromRemote(resp, err)) } - // only admin or the same logged user can create a token - if !isAdmin && user.ID != userID { - return "", util.NewErrBadRequest(errors.Errorf("logged in user cannot create token for another user")) - } if _, ok := user.Tokens[req.TokenName]; ok { return "", util.NewErrBadRequest(errors.Errorf("user %q already have a token with name %q", userRef, req.TokenName)) } @@ -161,6 +165,14 @@ type CreateUserLARequest struct { } func (h *ActionHandler) CreateUserLA(ctx context.Context, req *CreateUserLARequest) (*cstypes.LinkedAccount, error) { + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeUpdateUser, req.UserRef) + if err != nil { + return nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + userRef := req.UserRef user, resp, err := h.configstoreClient.GetUser(ctx, userRef) if err != nil { @@ -219,6 +231,14 @@ func (h *ActionHandler) CreateUserLA(ctx context.Context, req *CreateUserLAReque } func (h *ActionHandler) UpdateUserLA(ctx context.Context, userRef string, la *cstypes.LinkedAccount) error { + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeUpdateUser, userRef) + if err != nil { + return errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) + } + user, resp, err := h.configstoreClient.GetUser(ctx, userRef) if err != nil { return errors.Errorf("failed to get user %q: %w", userRef, ErrFromRemote(resp, err)) @@ -253,8 +273,8 @@ func (h *ActionHandler) UpdateUserLA(ctx context.Context, userRef string, la *cs return nil } -// RefreshLinkedAccount refreshed the linked account oauth2 access token and update linked account in the configstore -func (h *ActionHandler) RefreshLinkedAccount(ctx context.Context, rs *cstypes.RemoteSource, userName string, la *cstypes.LinkedAccount) (*cstypes.LinkedAccount, error) { +// refreshLinkedAccount refreshed the linked account oauth2 access token and update linked account in the configstore +func (h *ActionHandler) refreshLinkedAccount(ctx context.Context, rs *cstypes.RemoteSource, userName string, la *cstypes.LinkedAccount) (*cstypes.LinkedAccount, error) { switch rs.AuthType { case cstypes.RemoteSourceAuthTypeOauth2: // refresh access token if expired @@ -285,7 +305,7 @@ func (h *ActionHandler) RefreshLinkedAccount(ctx context.Context, rs *cstypes.Re // GetGitSource is a wrapper around common.GetGitSource that will also refresh // the oauth2 access token and update the linked account when needed func (h *ActionHandler) GetGitSource(ctx context.Context, rs *cstypes.RemoteSource, userName string, la *cstypes.LinkedAccount) (gitsource.GitSource, error) { - la, err := h.RefreshLinkedAccount(ctx, rs, userName, la) + la, err := h.refreshLinkedAccount(ctx, rs, userName, la) if err != nil { return nil, err } @@ -753,8 +773,12 @@ func (h *ActionHandler) HandleOauth2Callback(ctx context.Context, code, state st } func (h *ActionHandler) DeleteUser(ctx context.Context, userRef string) error { - if !h.IsUserAdmin(ctx) { - return errors.Errorf("user not logged in") + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeDeleteUser, userRef) + if err != nil { + return errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) } resp, err := h.configstoreClient.DeleteUser(ctx, userRef) @@ -765,24 +789,15 @@ func (h *ActionHandler) DeleteUser(ctx context.Context, userRef string) error { } func (h *ActionHandler) DeleteUserLA(ctx context.Context, userRef, laID string) error { - 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) + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeUpdateUser, userRef) if err != nil { - return errors.Errorf("failed to get user %q: %w", userRef, ErrFromRemote(resp, err)) + return errors.Errorf("failed to determine authorization: %w", err) } - - // 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")) + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) } - resp, err = h.configstoreClient.DeleteUserLA(ctx, userRef, laID) + resp, err := h.configstoreClient.DeleteUserLA(ctx, userRef, laID) if err != nil { return errors.Errorf("failed to delete user linked account: %w", ErrFromRemote(resp, err)) } @@ -790,24 +805,15 @@ func (h *ActionHandler) DeleteUserLA(ctx context.Context, userRef, laID string) } func (h *ActionHandler) DeleteUserToken(ctx context.Context, userRef, tokenName string) error { - 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) + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeUpdateUser, userRef) if err != nil { - return errors.Errorf("failed to get user %q: %w", userRef, ErrFromRemote(resp, err)) + return errors.Errorf("failed to determine authorization: %w", err) } - - // 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")) + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) } - resp, err = h.configstoreClient.DeleteUserToken(ctx, userRef, tokenName) + resp, err := h.configstoreClient.DeleteUserToken(ctx, userRef, tokenName) if err != nil { return errors.Errorf("failed to delete user token: %w", ErrFromRemote(resp, err)) } @@ -828,6 +834,16 @@ type UserCreateRunRequest struct { } func (h *ActionHandler) UserCreateRun(ctx context.Context, req *UserCreateRunRequest) error { + curUserID := h.CurrentUserID(ctx) + + authorized, err := h.CanDoUserAction(ctx, cstypes.ActionTypeCreateRun, curUserID) + if err != nil { + return errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return util.NewErrForbidden(errors.Errorf("user not authorized")) + } + prRefRegexes := []*regexp.Regexp{} for _, res := range req.PullRequestRefRegexes { re, err := regexp.Compile(res) @@ -837,8 +853,6 @@ func (h *ActionHandler) UserCreateRun(ctx context.Context, req *UserCreateRunReq prRefRegexes = append(prRefRegexes, re) } - curUserID := h.CurrentUserID(ctx) - user, resp, err := h.configstoreClient.GetUser(ctx, curUserID) if err != nil { return errors.Errorf("failed to get user %q: %w", curUserID, ErrFromRemote(resp, err)) @@ -853,7 +867,7 @@ func (h *ActionHandler) UserCreateRun(ctx context.Context, req *UserCreateRunReq return util.NewErrBadRequest(errors.Errorf("wrong repo path: %q", req.RepoPath)) } if repoParts[0] != user.ID { - return util.NewErrUnauthorized(errors.Errorf("repo %q not owned", req.RepoPath)) + return util.NewErrForbidden(errors.Errorf("repo %q not owned", req.RepoPath)) } branch := req.Branch diff --git a/internal/services/gateway/action/variable.go b/internal/services/gateway/action/variable.go index bb58701a8..06d154403 100644 --- a/internal/services/gateway/action/variable.go +++ b/internal/services/gateway/action/variable.go @@ -34,6 +34,14 @@ type GetVariablesRequest struct { } func (h *ActionHandler) GetVariables(ctx context.Context, req *GetVariablesRequest) ([]*csapitypes.Variable, []*csapitypes.Secret, error) { + authorized, err := h.CanDoVariableAction(ctx, cstypes.ActionTypeGetVariable, req.ParentType, req.ParentRef) + if err != nil { + return nil, nil, errors.Errorf("failed to determine authorization: %w", err) + } + if !authorized { + return nil, nil, util.NewErrForbidden(errors.Errorf("user not authorized")) + } + var csvars []*csapitypes.Variable var cssecrets []*csapitypes.Secret @@ -80,11 +88,11 @@ type CreateVariableRequest struct { } func (h *ActionHandler) CreateVariable(ctx context.Context, req *CreateVariableRequest) (*csapitypes.Variable, []*csapitypes.Secret, error) { - isVariableOwner, err := h.IsVariableOwner(ctx, req.ParentType, req.ParentRef) + authorized, err := h.CanDoVariableAction(ctx, cstypes.ActionTypeCreateVariable, req.ParentType, req.ParentRef) if err != nil { - return nil, nil, errors.Errorf("failed to determine ownership: %w", err) + return nil, nil, errors.Errorf("failed to determine authorization: %w", err) } - if !isVariableOwner { + if !authorized { return nil, nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -153,11 +161,11 @@ type UpdateVariableRequest struct { } func (h *ActionHandler) UpdateVariable(ctx context.Context, req *UpdateVariableRequest) (*csapitypes.Variable, []*csapitypes.Secret, error) { - isVariableOwner, err := h.IsVariableOwner(ctx, req.ParentType, req.ParentRef) + authorized, err := h.CanDoVariableAction(ctx, cstypes.ActionTypeUpdateVariable, req.ParentType, req.ParentRef) if err != nil { - return nil, nil, errors.Errorf("failed to determine ownership: %w", err) + return nil, nil, errors.Errorf("failed to determine authorization: %w", err) } - if !isVariableOwner { + if !authorized { return nil, nil, util.NewErrForbidden(errors.Errorf("user not authorized")) } @@ -215,11 +223,11 @@ func (h *ActionHandler) UpdateVariable(ctx context.Context, req *UpdateVariableR } func (h *ActionHandler) DeleteVariable(ctx context.Context, parentType cstypes.ConfigType, parentRef, name string) error { - isVariableOwner, err := h.IsVariableOwner(ctx, parentType, parentRef) + authorized, err := h.CanDoVariableAction(ctx, cstypes.ActionTypeDeleteVariable, parentType, parentRef) if err != nil { - return errors.Errorf("failed to determine ownership: %w", err) + return errors.Errorf("failed to determine authorization: %w", err) } - if !isVariableOwner { + if !authorized { return util.NewErrForbidden(errors.Errorf("user not authorized")) }