go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/swarming/server/rpcs/swarming_get_permissions.go (about) 1 // Copyright 2023 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package rpcs 16 17 import ( 18 "context" 19 "strings" 20 21 "go.chromium.org/luci/server/auth/realms" 22 23 apipb "go.chromium.org/luci/swarming/proto/api_v2" 24 "go.chromium.org/luci/swarming/server/acls" 25 ) 26 27 // GetPermissions implements the corresponding RPC method. 28 func (srv *SwarmingServer) GetPermissions(ctx context.Context, req *apipb.PermissionsRequest) (*apipb.ClientPermissions, error) { 29 state := State(ctx) 30 31 var pools []string 32 for _, tag := range req.Tags { 33 if pool, ok := strings.CutPrefix(tag, "pool:"); ok { 34 pools = append(pools, pool) 35 } 36 } 37 38 var internalErr error 39 40 checkServerPerm := func(perm realms.Permission) bool { 41 if internalErr != nil { 42 return false 43 } 44 res := state.ACL.CheckServerPerm(ctx, perm) 45 if res.InternalError { 46 internalErr = res.ToGrpcErr() 47 } 48 return res.Permitted 49 } 50 51 checkPoolsPerm := func(perm realms.Permission) bool { 52 if internalErr != nil { 53 return false 54 } 55 if len(pools) == 0 { 56 return checkServerPerm(perm) 57 } 58 res := state.ACL.CheckAllPoolsPerm(ctx, pools, perm) 59 if res.InternalError { 60 internalErr = res.ToGrpcErr() 61 } 62 return res.Permitted 63 } 64 65 checkTaskPerm := func(taskInfo acls.TaskAuthInfo, perm realms.Permission) bool { 66 if internalErr != nil { 67 return false 68 } 69 res := state.ACL.CheckTaskPerm(ctx, taskInfo, perm) 70 if res.InternalError { 71 internalErr = res.ToGrpcErr() 72 } 73 return res.Permitted 74 } 75 76 checkBotPerm := func(perm realms.Permission) bool { 77 if internalErr != nil { 78 return false 79 } 80 if req.BotId == "" { 81 return checkServerPerm(perm) 82 } 83 res := state.ACL.CheckBotPerm(ctx, req.BotId, perm) 84 if res.InternalError { 85 internalErr = res.ToGrpcErr() 86 } 87 return res.Permitted 88 } 89 90 poolsWithPerm := func(perm realms.Permission) []string { 91 if internalErr != nil { 92 return nil 93 } 94 filtered, err := state.ACL.FilterPoolsByPerm(ctx, state.Config.Pools(), perm) 95 if err != nil { 96 internalErr = err 97 return nil 98 } 99 return filtered 100 } 101 102 resp := &apipb.ClientPermissions{ 103 DeleteBot: checkBotPerm(acls.PermPoolsDeleteBot), 104 DeleteBots: checkPoolsPerm(acls.PermPoolsDeleteBot), 105 TerminateBot: checkBotPerm(acls.PermPoolsTerminateBot), 106 GetBootstrapToken: checkServerPerm(acls.PermPoolsCreateBot), 107 GetConfigs: false, // deprecated and unused 108 PutConfigs: false, // deprecated and unused 109 CancelTask: false, // see below 110 CancelTasks: checkPoolsPerm(acls.PermPoolsCancelTask), 111 ListBots: poolsWithPerm(acls.PermPoolsListBots), 112 ListTasks: poolsWithPerm(acls.PermPoolsListTasks), 113 } 114 115 // If we are given a task ID, we need to check if we can cancel this specific 116 // task. If there's no task ID, we need to check we can cancel *any* task in 117 // the specified pools (and we already did this check, see CancelTasks). These 118 // are two different permissions (PermTasksCancel vs PermPoolsCancelTask) 119 // since mass-canceling needs to be more restricted compared to canceling 120 // tasks one by one. 121 if req.TaskId != "" { 122 taskRequest, err := FetchTaskRequest(ctx, req.TaskId) 123 if err != nil { 124 return nil, err 125 } 126 resp.CancelTask = checkTaskPerm(taskRequest.TaskAuthInfo(), acls.PermTasksCancel) 127 } else { 128 resp.CancelTask = resp.CancelTasks 129 } 130 131 if internalErr != nil { 132 return nil, internalErr 133 } 134 return resp, nil 135 }