File: config.go

package info (click to toggle)
golang-github-azure-go-autorest 14.2.0%2Bgit20220726.711dde1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,320 kB
  • sloc: makefile: 15
file content (151 lines) | stat: -rw-r--r-- 4,934 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
package adal

// Copyright 2017 Microsoft Corporation
//
//  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.

import (
	"errors"
	"fmt"
	"net/url"
)

const (
	activeDirectoryEndpointTemplate = "%s/oauth2/%s%s"
)

// OAuthConfig represents the endpoints needed
// in OAuth operations
type OAuthConfig struct {
	AuthorityEndpoint  url.URL `json:"authorityEndpoint"`
	AuthorizeEndpoint  url.URL `json:"authorizeEndpoint"`
	TokenEndpoint      url.URL `json:"tokenEndpoint"`
	DeviceCodeEndpoint url.URL `json:"deviceCodeEndpoint"`
}

// IsZero returns true if the OAuthConfig object is zero-initialized.
func (oac OAuthConfig) IsZero() bool {
	return oac == OAuthConfig{}
}

func validateStringParam(param, name string) error {
	if len(param) == 0 {
		return fmt.Errorf("parameter '" + name + "' cannot be empty")
	}
	return nil
}

// NewOAuthConfig returns an OAuthConfig with tenant specific urls
func NewOAuthConfig(activeDirectoryEndpoint, tenantID string) (*OAuthConfig, error) {
	apiVer := "1.0"
	return NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID, &apiVer)
}

// NewOAuthConfigWithAPIVersion returns an OAuthConfig with tenant specific urls.
// If apiVersion is not nil the "api-version" query parameter will be appended to the endpoint URLs with the specified value.
func NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, tenantID string, apiVersion *string) (*OAuthConfig, error) {
	if err := validateStringParam(activeDirectoryEndpoint, "activeDirectoryEndpoint"); err != nil {
		return nil, err
	}
	api := ""
	// it's legal for tenantID to be empty so don't validate it
	if apiVersion != nil {
		if err := validateStringParam(*apiVersion, "apiVersion"); err != nil {
			return nil, err
		}
		api = fmt.Sprintf("?api-version=%s", *apiVersion)
	}
	u, err := url.Parse(activeDirectoryEndpoint)
	if err != nil {
		return nil, err
	}
	authorityURL, err := u.Parse(tenantID)
	if err != nil {
		return nil, err
	}
	authorizeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "authorize", api))
	if err != nil {
		return nil, err
	}
	tokenURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "token", api))
	if err != nil {
		return nil, err
	}
	deviceCodeURL, err := u.Parse(fmt.Sprintf(activeDirectoryEndpointTemplate, tenantID, "devicecode", api))
	if err != nil {
		return nil, err
	}

	return &OAuthConfig{
		AuthorityEndpoint:  *authorityURL,
		AuthorizeEndpoint:  *authorizeURL,
		TokenEndpoint:      *tokenURL,
		DeviceCodeEndpoint: *deviceCodeURL,
	}, nil
}

// MultiTenantOAuthConfig provides endpoints for primary and aulixiary tenant IDs.
type MultiTenantOAuthConfig interface {
	PrimaryTenant() *OAuthConfig
	AuxiliaryTenants() []*OAuthConfig
}

// OAuthOptions contains optional OAuthConfig creation arguments.
type OAuthOptions struct {
	APIVersion string
}

func (c OAuthOptions) apiVersion() string {
	if c.APIVersion != "" {
		return fmt.Sprintf("?api-version=%s", c.APIVersion)
	}
	return "1.0"
}

// NewMultiTenantOAuthConfig creates an object that support multitenant OAuth configuration.
// See https://docs.microsoft.com/en-us/azure/azure-resource-manager/authenticate-multi-tenant for more information.
func NewMultiTenantOAuthConfig(activeDirectoryEndpoint, primaryTenantID string, auxiliaryTenantIDs []string, options OAuthOptions) (MultiTenantOAuthConfig, error) {
	if len(auxiliaryTenantIDs) == 0 || len(auxiliaryTenantIDs) > 3 {
		return nil, errors.New("must specify one to three auxiliary tenants")
	}
	mtCfg := multiTenantOAuthConfig{
		cfgs: make([]*OAuthConfig, len(auxiliaryTenantIDs)+1),
	}
	apiVer := options.apiVersion()
	pri, err := NewOAuthConfigWithAPIVersion(activeDirectoryEndpoint, primaryTenantID, &apiVer)
	if err != nil {
		return nil, fmt.Errorf("failed to create OAuthConfig for primary tenant: %v", err)
	}
	mtCfg.cfgs[0] = pri
	for i := range auxiliaryTenantIDs {
		aux, err := NewOAuthConfig(activeDirectoryEndpoint, auxiliaryTenantIDs[i])
		if err != nil {
			return nil, fmt.Errorf("failed to create OAuthConfig for tenant '%s': %v", auxiliaryTenantIDs[i], err)
		}
		mtCfg.cfgs[i+1] = aux
	}
	return mtCfg, nil
}

type multiTenantOAuthConfig struct {
	// first config in the slice is the primary tenant
	cfgs []*OAuthConfig
}

func (m multiTenantOAuthConfig) PrimaryTenant() *OAuthConfig {
	return m.cfgs[0]
}

func (m multiTenantOAuthConfig) AuxiliaryTenants() []*OAuthConfig {
	return m.cfgs[1:]
}