#nullable enable
using System;
using System.Linq;
using GitHub.Actions.WorkflowParser.ObjectTemplating;
namespace GitHub.Actions.WorkflowParser.Conversion
{
internal static class PermissionsHelper
{
///
/// Validates permissions requested in a reusable workflow do not exceed allowed permissions
///
/// The template context
/// The reusable workflow job
/// (Optional) Used when formatting errors related to an embedded job within the reusable workflow
/// The permissions within the reusable workflow file. These may be defined either at the root of the file, or may be defined on a job within the file.
/// (Optional) The max permissions explicitly allowed by the caller
/// The default permissions policy
/// Indicates whether the reusable workflow exists within the same trust boundary (e.g. enterprise/organization) as a the root workflow
internal static void ValidateEmbeddedPermissions(
TemplateContext context,
ReusableWorkflowJob workflowJob,
IJob? embeddedJob,
Permissions requested,
Permissions? explicitMax,
string permissionsPolicy,
bool isTrusted)
{
if (requested == null)
{
return;
}
var effectiveMax = explicitMax ?? CreatePermissionsFromPolicy(context, permissionsPolicy, includeIdToken: isTrusted, includeModels: context.GetFeatures().AllowModelsPermission);
if (requested.ViolatesMaxPermissions(effectiveMax, out var permissionLevelViolations))
{
var requestedStr = string.Join(", ", permissionLevelViolations.Select(x => x.RequestedPermissionLevelString()));
var allowedStr = string.Join(", ", permissionLevelViolations.Select(x => x.AllowedPermissionLevelString()));
if (embeddedJob != null)
{
context.Error(workflowJob.Id, $"Error calling workflow '{workflowJob.Ref}'. The nested job '{embeddedJob.Id!.Value}' is requesting '{requestedStr}', but is only allowed '{allowedStr}'.");
}
else
{
context.Error(workflowJob.Id, $"Error calling workflow '{workflowJob.Ref}'. The workflow is requesting '{requestedStr}', but is only allowed '{allowedStr}'.");
}
}
}
///
/// Creates permissions based on policy
///
/// The template context
/// The permissions policy
/// Indicates whether the permissions should include an ID token
private static Permissions CreatePermissionsFromPolicy(
TemplateContext context,
string permissionsPolicy,
bool includeIdToken,
bool includeModels)
{
switch (permissionsPolicy)
{
case WorkflowConstants.PermissionsPolicy.LimitedRead:
return new Permissions(PermissionLevel.NoAccess, includeIdToken: false, includeAttestations: false, includeModels: false)
{
Contents = PermissionLevel.Read,
Packages = PermissionLevel.Read,
};
case WorkflowConstants.PermissionsPolicy.Write:
return new Permissions(PermissionLevel.Write, includeIdToken: includeIdToken, includeAttestations: true, includeModels: includeModels);
default:
throw new ArgumentException($"Unexpected permission policy: '{permissionsPolicy}'");
}
}
}
}