src/graphservice/GraphEndpoint.ps1

# Copyright 2020, Adam Edwards
#
# 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.

enum GraphCloud {
    Public
    ChinaCloud
    GermanyCloud
    USGovernmentCloud
    Custom
}

enum GraphType {
    MSGraph
    AADGraph
}

enum GraphAuthProtocol {
    Default
    v1
    v2
}

ScriptClass GraphEndpoint {
    static {
        const DefaultGraphAPIVersion 'v1.0'
        # Note: this hash table, as well as the nested hash tables,
        # Must be protected -- do *NOT* give references to them outside
        # the static methods of this class -- that includes preventing
        # instance methods from having access! If callers need access to,
        # the hash tables, clone them. Without that, callers can modify
        # this shared state! This is an issue for any objects that are not
        # treated by value. Strings and integers for instance are ok, any
        # object type is not, and must be handled with care.
        $MSGraphCloudEndpoints = @{
            [GraphCloud]::Public = @{
                Authentication='https://login.microsoftonline.com'
                Graph='https://graph.microsoft.com'
                AuthProtocol=[GraphAuthProtocol]::v2
            }
            [GraphCloud]::ChinaCloud = @{
                Authentication='https://login.chinacloudapi.cn'
                Graph='https://microsoftgraph.chinacloudapi.cn'
                AuthProtocol=[GraphAuthProtocol]::v2
            }
            [GraphCloud]::GermanyCloud = @{
                Authentication='https://login.microsoftonline.de'
                Graph='https://graph.microsoft.de'
                AuthProtocol=[GraphAuthProtocol]::v2
            }
            [GraphCloud]::USGovernmentCloud = @{
                Authentication='https://login.microsoftonline.us'
                Graph='https://graph.microsoft.us'
                AuthProtocol=[GraphAuthProtocol]::v2
            }
        }

        $AADGraphCloudEndpoints = @{
            Authentication = 'https://login.microsoftonline.com'
            Graph='https://graph.windows.net'
            AuthProtocol=[GraphAuthProtocol]::v1
        }

        function GetCloudEndpoint([GraphCloud] $cloud, [GraphType] $graphType) {
            # We *MUST* clone these -- otherwise callers have a reference to the
            # shared instance in the static class, and they can overwrite it!
            if ($graphType -eq [GraphType]::MSGraph) {
                $this.MSGraphCloudEndpoints[$cloud].Clone()
            } else {
                $this.AADGraphCloudEndpoints.Clone()
            }
        }

        function GetAuthProtocol($specifiedAuthProtocol, $cloud, $graphType) {
            if ( $specifiedAuthProtocol -eq $null ) {
                throw "Invalid auth protocol -- auth protocol must not be null"
            }

            $authProtocol = if ( $specifiedAuthProtocol -ne ([GraphAuthProtocol]::Default) ) {
                $specifiedAuthProtocol
            } else {
                $cloudEndpoint = GetCloudEndpoint $cloud $graphType
                if ( $cloudEndpoint ) {
                    $cloudEndpoint.AuthProtocol
                } else {
                    [GraphAuthProtocol]::v2
                }
            }

            $authProtocol
        }

        function IsWellKnownCloud([string] $cloud) {
            $cloud -ne 'Custom' -and ( $cloud -in [GraphCloud]::GetNames([GraphCloud]) )
        }
    }

    $Authentication = $null
    $Graph = $null
    $Type = ([GraphType]::MSGraph)
    $Cloud = ([GraphCloud]::Custom)
    $AuthProtocol = $null
    $GraphResourceUri = $null

    function __initialize {
        [cmdletbinding()]
        param (
            [GraphCloud] $cloud,
            [GraphType] $graphType = [GraphType]::MSGraph,
            [Uri] $GraphEndpoint,
            [Uri] $AuthenticationEndpoint,
            $authProtocol = $null,
            [Uri] $graphResourceUri
        )

        $this.Type = $GraphType
        $this.Cloud = $cloud
        $endpointData = if ($GraphEndpoint -eq $null) {
            $cloudEndpoint = $this.scriptclass |=> GetCloudEndpoint $cloud $graphType
            if ( $authProtocol ) {
                $cloudEndpoint.AuthProtocol = $authProtocol
            }
            $cloudEndpoint
        } else {
            @{
                Graph=$GraphEndpoint
                Authentication=$AuthenticationEndpoint
                AuthProtocol=($this.scriptclass |=> GetAuthProtocol $authProtocol $cloud $graphType)
            }
        }

        $this.Authentication = new-object Uri $endpointData.Authentication
        $this.Graph = new-object Uri $endpointData.Graph
        $this.AuthProtocol = $endpointData.AuthProtocol
        $this.GraphResourceUri = if ( $graphResourceUri ) { $graphResourceUri } else { $this.Graph }
    }

    function GetAuthUri($tenantName, $allowMSA) {
        $tenantSegment = if ( $allowMSA -and ! $tenantName ) {
            'common'
        } elseif ( $tenantName ) {
            $tenantName
        } else {
            'organizations'
        }

        $components = @($this.Authentication.tostring().trimend('/'))
        if ( $tenantSegment ) {
            $components += $tenantSegment
        }

        $components -join '/'
    }
}