Public/role.ps1

# Dynamic roles and their API permissions (the allow/deny rules that decide which
# API commands a role's accounts may call).

function Get-CSRole {
    <#
    .SYNOPSIS
        Lists dynamic roles.

    .DESCRIPTION
        Wraps listRoles. Filter by id, name, or role type (Admin, DomainAdmin,
        ResourceAdmin, or User).

    .PARAMETER Id
        Filter by role ID

    .PARAMETER Name
        Filter by role name

    .PARAMETER Type
        Filter by role type: Admin, DomainAdmin, ResourceAdmin, or User

    .PARAMETER Keyword
        Filter by keyword

    .PARAMETER Page
        Page number of results to return

    .PARAMETER PageSize
        Number of results per page

    .EXAMPLE
        Get-CSRole
        Lists every dynamic role.

    .EXAMPLE
        Get-CSRole -Type DomainAdmin
        Lists the domain-admin roles.
    #>

    [CmdletBinding()]
    param(
        [string]$Id,

        [string]$Name,

        [ValidateSet('Admin', 'DomainAdmin', 'ResourceAdmin', 'User')]
        [string]$Type,

        [string]$Keyword,

        [int]$Page,

        [int]$PageSize
    )

    $apiParams = @{}
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Id = 'id'; Name = 'name'; Type = 'type'; Keyword = 'keyword'; Page = 'page'; PageSize = 'pagesize'
    })
    ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listRoles' -Parameters $apiParams) -Command 'listRoles'
}

function New-CSRole {
    <#
    .SYNOPSIS
        Creates a dynamic role.

    .DESCRIPTION
        Wraps createRole. Give -Type for a fresh role, or -RoleId to clone an
        existing role's permissions. A new role starts with the default rule set for
        its type; add rules with Add-CSRolePermission.

    .PARAMETER Name
        Name for the role

    .PARAMETER Type
        The base role type: Admin, DomainAdmin, ResourceAdmin, or User

    .PARAMETER RoleId
        Clone from this existing role instead of a base type

    .PARAMETER Description
        A description for the role

    .PARAMETER IsPublic
        Whether the role is visible to all domains

    .EXAMPLE
        New-CSRole -Name 'read-only-ops' -Type User -Description 'Operators, read-only'
        Creates a user-based role.

    .EXAMPLE
        New-CSRole -Name 'domain-admin-copy' -RoleId $existingRoleId
        Clones an existing role's permissions into a new role.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [ValidateSet('Admin', 'DomainAdmin', 'ResourceAdmin', 'User')]
        [string]$Type,

        [string]$RoleId,

        [string]$Description,

        [Nullable[bool]]$IsPublic
    )

    if (-not $PSBoundParameters.ContainsKey('Type') -and -not $PSBoundParameters.ContainsKey('RoleId')) {
        throw 'Specify -Type for a new role, or -RoleId to clone an existing one.'
    }
    $apiParams = @{ name = $Name }
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Type = 'type'; RoleId = 'roleid'; Description = 'description'
    })
    if ($PSBoundParameters.ContainsKey('IsPublic')) { $apiParams['ispublic'] = ([bool]$IsPublic).ToString().ToLowerInvariant() }
    if ($PSCmdlet.ShouldProcess("role $Name", 'Create')) {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'createRole' -Parameters $apiParams) -Command 'createRole'
    }
}

function Import-CSRole {
    <#
    .SYNOPSIS
        Imports a role from a set of rule permissions.

    .DESCRIPTION
        Wraps importRole, creating a role with a full ordered rule set in one call.
        -Rules is an array of hashtables, each with Rule (an API name or wildcard),
        Permission (allow or deny), and optionally Description; the array order is the
        evaluation order.

    .PARAMETER Name
        Name for the role

    .PARAMETER Rules
        An array of @{ Rule = 'listVirtualMachines'; Permission = 'allow'; Description = '...' } entries

    .PARAMETER Type
        The base role type: Admin, DomainAdmin, ResourceAdmin, or User

    .PARAMETER Description
        A description for the role

    .PARAMETER Forced
        Overwrite an existing role of the same name

    .PARAMETER IsPublic
        Whether the role is visible to all domains

    .EXAMPLE
        Import-CSRole -Name 'vm-operators' -Type User -Rules @(
            @{ Rule = 'list*'; Permission = 'allow' },
            @{ Rule = 'deployVirtualMachine'; Permission = 'allow' },
            @{ Rule = '*'; Permission = 'deny' })
        Imports a role that allows listing and VM deployment and denies everything else.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true)]
        [string]$Name,

        [Parameter(Mandatory = $true)]
        [hashtable[]]$Rules,

        [ValidateSet('Admin', 'DomainAdmin', 'ResourceAdmin', 'User')]
        [string]$Type,

        [string]$Description,

        [switch]$Forced,

        [Nullable[bool]]$IsPublic
    )

    $apiParams = @{ name = $Name }
    Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
        Type = 'type'; Description = 'description'; Forced = 'forced'
    })
    if ($PSBoundParameters.ContainsKey('IsPublic')) { $apiParams['ispublic'] = ([bool]$IsPublic).ToString().ToLowerInvariant() }
    for ($i = 0; $i -lt $Rules.Count; $i++) {
        $entry = $Rules[$i]
        if (-not $entry.Rule -or -not $entry.Permission) { throw "Rules[$i] must have a Rule and a Permission." }
        $apiParams["rules[$i].rule"] = [string]$entry.Rule
        $apiParams["rules[$i].permission"] = [string]$entry.Permission
        if ($entry.Description) { $apiParams["rules[$i].description"] = [string]$entry.Description }
    }
    if ($PSCmdlet.ShouldProcess("role $Name", "Import with $($Rules.Count) rules")) {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'importRole' -Parameters $apiParams) -Command 'importRole'
    }
}

function Set-CSRole {
    <#
    .SYNOPSIS
        Updates a role's attributes.

    .DESCRIPTION
        Wraps updateRole. Only the attributes you supply are changed. Accepts role
        objects on the pipeline.

    .PARAMETER Id
        The role to update. Binds from a piped role's id.

    .PARAMETER Name
        New name

    .PARAMETER Type
        New base role type: Admin, DomainAdmin, ResourceAdmin, or User

    .PARAMETER Description
        New description

    .PARAMETER IsPublic
        Whether the role is visible to all domains

    .EXAMPLE
        Set-CSRole -Id $roleId -Description 'Operators, read-only (updated)'
        Updates a role's description.

    .EXAMPLE
        Get-CSRole -Name 'read-only-ops' | Set-CSRole -IsPublic $false
        Makes a role private.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('RoleId')]
        [string]$Id,

        [string]$Name,

        [ValidateSet('Admin', 'DomainAdmin', 'ResourceAdmin', 'User')]
        [string]$Type,

        [string]$Description,

        [Nullable[bool]]$IsPublic
    )

    process {
        $apiParams = @{ id = $Id }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            Name = 'name'; Type = 'type'; Description = 'description'
        })
        if ($PSBoundParameters.ContainsKey('IsPublic')) { $apiParams['ispublic'] = ([bool]$IsPublic).ToString().ToLowerInvariant() }
        if ($PSCmdlet.ShouldProcess("role $Id", 'Update')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'updateRole' -Parameters $apiParams) -Command 'updateRole'
        }
    }
}

function Remove-CSRole {
    <#
    .SYNOPSIS
        Deletes a role.

    .DESCRIPTION
        Wraps deleteRole. The role must not be assigned to any accounts. Accepts role
        objects on the pipeline.

    .PARAMETER Id
        The role to delete. Binds from a piped role's id.

    .EXAMPLE
        Remove-CSRole -Id $roleId
        Deletes a role after confirmation.

    .EXAMPLE
        Get-CSRole -Name 'obsolete-role' | Remove-CSRole
        Deletes a role located by name.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('RoleId')]
        [string]$Id
    )

    process {
        if ($PSCmdlet.ShouldProcess("role $Id", 'Delete')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'deleteRole' -Parameters @{ id = $Id }) -Command 'deleteRole'
        }
    }
}

function Enable-CSRole {
    <#
    .SYNOPSIS
        Enables a role.

    .DESCRIPTION
        Wraps enableRole, so its accounts' API permissions take effect again. Accepts
        role objects on the pipeline.

    .PARAMETER Id
        The role to enable. Binds from a piped role's id.

    .EXAMPLE
        Enable-CSRole -Id $roleId
        Enables a role.

    .EXAMPLE
        Get-CSRole -Name 'read-only-ops' | Enable-CSRole
        Enables a role located by name.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('RoleId')]
        [string]$Id
    )

    process {
        if ($PSCmdlet.ShouldProcess("role $Id", 'Enable')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'enableRole' -Parameters @{ id = $Id }) -Command 'enableRole'
        }
    }
}

function Disable-CSRole {
    <#
    .SYNOPSIS
        Disables a role.

    .DESCRIPTION
        Wraps disableRole. A disabled role denies its accounts all dynamic-role API
        access. Accepts role objects on the pipeline.

    .PARAMETER Id
        The role to disable. Binds from a piped role's id.

    .EXAMPLE
        Disable-CSRole -Id $roleId
        Disables a role.

    .EXAMPLE
        Get-CSRole -Name 'read-only-ops' | Disable-CSRole
        Disables a role located by name.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('RoleId')]
        [string]$Id
    )

    process {
        if ($PSCmdlet.ShouldProcess("role $Id", 'Disable')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'disableRole' -Parameters @{ id = $Id }) -Command 'disableRole'
        }
    }
}

function Get-CSRolePermission {
    <#
    .SYNOPSIS
        Lists a role's API permissions.

    .DESCRIPTION
        Wraps listRolePermissions, the ordered allow/deny rules for a role. Accepts
        role objects on the pipeline.

    .PARAMETER RoleId
        The role whose permissions to list. Binds from a piped role's id.

    .EXAMPLE
        Get-CSRolePermission -RoleId $roleId
        Lists a role's permission rules in evaluation order.

    .EXAMPLE
        Get-CSRole -Name 'vm-operators' | Get-CSRolePermission
        Lists the rules of a role located by name.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [string]$RoleId
    )

    process {
        ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'listRolePermissions' -Parameters @{ roleid = $RoleId }) -Command 'listRolePermissions'
    }
}

function Add-CSRolePermission {
    <#
    .SYNOPSIS
        Adds an API permission rule to a role.

    .DESCRIPTION
        Wraps createRolePermission, appending an allow/deny rule for an API command
        (or a wildcard such as 'list*') to a role. New rules are appended last;
        reorder them with Set-CSRolePermission. Accepts role objects on the pipeline.

    .PARAMETER RoleId
        The role to add the rule to. Binds from a piped role's id.

    .PARAMETER Rule
        The API command name or wildcard the rule matches

    .PARAMETER Permission
        allow or deny

    .PARAMETER Description
        A description for the rule

    .EXAMPLE
        Add-CSRolePermission -RoleId $roleId -Rule 'deployVirtualMachine' -Permission allow
        Allows a role to deploy VMs.

    .EXAMPLE
        Get-CSRole -Name 'vm-operators' | Add-CSRolePermission -Rule 'destroyVirtualMachine' -Permission deny -Description 'No destroys'
        Denies VM destruction on a role located by name.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [string]$RoleId,

        [Parameter(Mandatory = $true)]
        [string]$Rule,

        [Parameter(Mandatory = $true)]
        [ValidateSet('allow', 'deny')]
        [string]$Permission,

        [string]$Description
    )

    process {
        $apiParams = @{ roleid = $RoleId; rule = $Rule; permission = $Permission }
        if ($PSBoundParameters.ContainsKey('Description')) { $apiParams['description'] = $Description }
        if ($PSCmdlet.ShouldProcess("role $RoleId", "$Permission '$Rule'")) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'createRolePermission' -Parameters $apiParams) -Command 'createRolePermission'
        }
    }
}

function Set-CSRolePermission {
    <#
    .SYNOPSIS
        Reorders (or re-permits) a role's API permission rules.

    .DESCRIPTION
        Wraps updateRolePermission. Give -RuleOrder with all of the role's permission
        IDs in the new evaluation order, or -RuleId plus -Permission to flip a single
        rule between allow and deny. Rule order matters: the first matching rule wins.
        Accepts role objects on the pipeline.

    .PARAMETER RoleId
        The role whose permissions to update. Binds from a piped role's id.

    .PARAMETER RuleOrder
        All permission IDs of the role, in the new evaluation order

    .PARAMETER RuleId
        A single permission ID to change (with -Permission)

    .PARAMETER Permission
        allow or deny, when changing a single -RuleId

    .EXAMPLE
        $rules = Get-CSRole -Name 'vm-operators' | Get-CSRolePermission
        Set-CSRolePermission -RoleId $roleId -RuleOrder ($rules.id)
        Reorders a role's rules to the given order.

    .EXAMPLE
        Set-CSRolePermission -RoleId $roleId -RuleId $permissionId -Permission deny
        Flips a single rule to deny.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('Id')]
        [string]$RoleId,

        [string[]]$RuleOrder,

        [string]$RuleId,

        [ValidateSet('allow', 'deny')]
        [string]$Permission
    )

    process {
        if (-not $PSBoundParameters.ContainsKey('RuleOrder') -and -not $PSBoundParameters.ContainsKey('RuleId')) {
            throw 'Specify -RuleOrder to reorder rules, or -RuleId with -Permission to change one rule.'
        }
        if ($PSBoundParameters.ContainsKey('RuleId') -and -not $PSBoundParameters.ContainsKey('Permission')) {
            throw '-Permission is required when changing a single -RuleId.'
        }
        $apiParams = @{ roleid = $RoleId }
        Add-CSOptionalParameter -ApiParameters $apiParams -BoundParameters $PSBoundParameters -Map ([ordered]@{
            RuleOrder = 'ruleorder'; RuleId = 'ruleid'; Permission = 'permission'
        })
        if ($PSCmdlet.ShouldProcess("role $RoleId", 'Update permissions')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'updateRolePermission' -Parameters $apiParams) -Command 'updateRolePermission'
        }
    }
}

function Remove-CSRolePermission {
    <#
    .SYNOPSIS
        Deletes an API permission rule from a role.

    .DESCRIPTION
        Wraps deleteRolePermission, removing a single permission rule by its ID (see
        Get-CSRolePermission). Accepts permission objects on the pipeline.

    .PARAMETER Id
        The permission rule to delete. Binds from a piped permission's id.

    .EXAMPLE
        Remove-CSRolePermission -Id $permissionId
        Deletes one permission rule after confirmation.

    .EXAMPLE
        Get-CSRole -Name 'vm-operators' | Get-CSRolePermission | Where-Object rule -eq 'destroyVirtualMachine' | Remove-CSRolePermission
        Removes a specific rule from a role.
    #>

    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
    param(
        [Parameter(Mandatory = $true, ValueFromPipelineByPropertyName = $true)]
        [Alias('PermissionId')]
        [string]$Id
    )

    process {
        if ($PSCmdlet.ShouldProcess("role permission $Id", 'Delete')) {
            ConvertFrom-CSResponse -Response (Invoke-CSApiRequest -Command 'deleteRolePermission' -Parameters @{ id = $Id }) -Command 'deleteRolePermission'
        }
    }
}