gateway: initial authorization
This commit is contained in:
parent
081ac8a44f
commit
a04dd62e91
|
@ -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
|
||||
}
|
|
@ -52,6 +52,10 @@ type CreateOrgRequest struct {
|
|||
}
|
||||
|
||||
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 == "" {
|
||||
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 {
|
||||
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 {
|
||||
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete org"))
|
||||
}
|
||||
|
|
|
@ -33,6 +33,18 @@ func (h *ActionHandler) GetProject(ctx context.Context, projectRef string) (*csa
|
|||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -47,6 +59,29 @@ type CreateProjectRequest struct {
|
|||
}
|
||||
|
||||
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) {
|
||||
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"))
|
||||
}
|
||||
|
||||
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)
|
||||
_, resp, err = h.configstoreClient.GetProject(ctx, projectPath)
|
||||
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))
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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")
|
||||
}
|
||||
|
||||
parentRef := req.ParentRef
|
||||
if parentRef == "" {
|
||||
// create project in current user namespace
|
||||
parentRef = path.Join("user", user.Name)
|
||||
}
|
||||
|
||||
p := &types.Project{
|
||||
Name: req.Name,
|
||||
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))
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
return ErrFromRemote(resp, err)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, ErrFromRemote(resp, errors.Wrapf(err, "failed to get user %q", req.CurrentUserID))
|
||||
|
|
|
@ -55,6 +55,10 @@ type CreateRemoteSourceRequest struct {
|
|||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
if !h.IsUserAdmin(ctx) {
|
||||
return errors.Errorf("user not admin")
|
||||
}
|
||||
|
||||
resp, err := h.configstoreClient.DeleteRemoteSource(ctx, rsRef)
|
||||
if err != nil {
|
||||
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete remote source"))
|
||||
|
|
|
@ -30,12 +30,20 @@ func (h *ActionHandler) GetRun(ctx context.Context, runID string) (*rsapi.RunRes
|
|||
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
|
||||
}
|
||||
|
||||
type GetRunsRequest struct {
|
||||
PhaseFilter []string
|
||||
Groups []string
|
||||
Group string
|
||||
LastRun bool
|
||||
ChangeGroups []string
|
||||
StartRunID string
|
||||
|
@ -44,7 +52,16 @@ type GetRunsRequest struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
return nil, ErrFromRemote(resp, err)
|
||||
}
|
||||
|
@ -62,7 +79,19 @@ type GetLogsRequest struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
return nil, ErrFromRemote(resp, err)
|
||||
}
|
||||
|
@ -86,6 +115,18 @@ type RunActionsRequest struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
case RunActionTypeRestart:
|
||||
rsreq := &rsapi.RunCreateRequest{
|
||||
|
@ -130,6 +171,18 @@ type RunTaskActionsRequest struct {
|
|||
}
|
||||
|
||||
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 {
|
||||
case RunTaskActionTypeApprove:
|
||||
rsreq := &rsapi.RunTaskActionsRequest{
|
||||
|
|
|
@ -72,6 +72,14 @@ type CreateSecretHandler struct {
|
|||
}
|
||||
|
||||
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) {
|
||||
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 rs *csapi.Secret
|
||||
var err error
|
||||
switch req.ParentType {
|
||||
case types.ConfigTypeProjectGroup:
|
||||
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 {
|
||||
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 err error
|
||||
switch parentType {
|
||||
case types.ConfigTypeProjectGroup:
|
||||
h.log.Infof("deleting project group secret")
|
||||
|
|
|
@ -41,6 +41,10 @@ func isAccessTokenExpired(expiresAt time.Time) bool {
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, ErrFromRemote(resp, err)
|
||||
|
@ -55,6 +59,10 @@ type GetUsersRequest struct {
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, ErrFromRemote(resp, err)
|
||||
|
@ -67,6 +75,10 @@ type CreateUserRequest struct {
|
|||
}
|
||||
|
||||
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 == "" {
|
||||
return nil, util.NewErrBadRequest(errors.Errorf("user name required"))
|
||||
}
|
||||
|
@ -490,10 +502,20 @@ func (h *ActionHandler) HandleRemoteSourceAuth(ctx context.Context, remoteSource
|
|||
switch requestType {
|
||||
case RemoteSourceRequestTypeCreateUserLA:
|
||||
req := req.(*CreateUserLARequest)
|
||||
|
||||
user, resp, err := h.configstoreClient.GetUser(ctx, req.UserRef)
|
||||
if err != nil {
|
||||
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
|
||||
for _, v := range user.LinkedAccounts {
|
||||
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 {
|
||||
if !h.IsUserAdmin(ctx) {
|
||||
return errors.Errorf("user not logged in")
|
||||
}
|
||||
|
||||
resp, err := h.configstoreClient.DeleteUser(ctx, userRef)
|
||||
if err != nil {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
return ErrFromRemote(resp, errors.Wrapf(err, "failed to delete user token"))
|
||||
}
|
||||
|
|
|
@ -80,6 +80,14 @@ type CreateVariableRequest struct {
|
|||
}
|
||||
|
||||
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) {
|
||||
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 {
|
||||
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
|
||||
switch parentType {
|
||||
case types.ConfigTypeProjectGroup:
|
||||
|
|
|
@ -313,9 +313,10 @@ func (h *RunsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
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
|
||||
if len(groups) == 0 {
|
||||
if group == "" {
|
||||
httpError(w, util.NewErrBadRequest(errors.Errorf("no groups specified")))
|
||||
return
|
||||
}
|
||||
|
@ -350,7 +351,7 @@ func (h *RunsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
areq := &action.GetRunsRequest{
|
||||
PhaseFilter: phaseFilter,
|
||||
Groups: groups,
|
||||
Group: group,
|
||||
LastRun: lastRun,
|
||||
ChangeGroups: changeGroups,
|
||||
StartRunID: start,
|
||||
|
|
|
@ -27,8 +27,11 @@ import (
|
|||
type GroupType string
|
||||
|
||||
const (
|
||||
// base groups
|
||||
GroupTypeProject GroupType = "project"
|
||||
GroupTypeUser GroupType = "user"
|
||||
|
||||
// sub groups
|
||||
GroupTypeBranch GroupType = "branch"
|
||||
GroupTypeTag GroupType = "tag"
|
||||
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))
|
||||
}
|
||||
|
||||
func ProjectIDFromRunGroup(group string) (string, error) {
|
||||
func GroupTypeIDFromRunGroup(group string) (GroupType, string, error) {
|
||||
pl := util.PathList(group)
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue