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  }