Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Initial API Classes #499

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions apps/managedidentity/managedidentity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.

/*
Package managedidentity provides a client for retrieval of Managed Identity applications.
The Managed Identity Client is used to acquire a token for managed identity assigned to
an azure resource such as Azure function, app service, virtual machine, etc. to acquire a token
without using credentials.
*/
package managedidentity

import (
"context"
"fmt"
"sync"

"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority"
)

const (
// AzureArc represents the source to acquire token for managed identity is Azure Arc.
AzureArc = 0
AndyOHart marked this conversation as resolved.
Show resolved Hide resolved

// DefaultToIMDS indicates that the source is defaulted to IMDS since no environment variables are set.
DefaultToIMDS = 1
)

// Client is a client that provides access to Managed Identity token calls.
type Client struct {
AuthParams authority.AuthParams // DO NOT EVER MAKE THIS A POINTER! See "Note" in New(). also may remove from here
cacheAccessorMu *sync.RWMutex
// base ops.HTTPClient
// managedIdentityType Type
// Token *oauth.Client
// pmanager manager // todo : expose the manager from base.
// cacheAccessor cache.ExportReplace
}

// clientOptions are optional settings for New(). These options are set using various functions
// returning Option calls.
type clientOptions struct {
claims string // bypasses cache, does nothing else
httpClient ops.HTTPClient
// disableInstanceDiscovery bool // always false
// clientId string
}

type withClaimsOption struct{ Claims string }
type withHTTPClientOption struct{ HttpClient ops.HTTPClient }

// Option is an optional argument to New().
type Option interface{ apply(*clientOptions) }
type ClientOption interface{ ClientOption() }
type AcquireTokenOption interface{ AcquireTokenOption() }

// Source represents the managed identity sources supported.
type Source int

type systemAssignedValue string

type ID interface {
value() string
}

func SystemAssigned() ID {
return systemAssignedValue("")
}

type ClientID string
type ObjectID string
type ResourceID string

func (s systemAssignedValue) value() string { return string(s) }
func (c ClientID) value() string { return string(c) }
func (o ObjectID) value() string { return string(o) }
func (r ResourceID) value() string { return string(r) }

func (w withClaimsOption) AcquireTokenOption() {}
func (w withHTTPClientOption) AcquireTokenOption() {}
func (w withHTTPClientOption) apply(opts *clientOptions) { opts.httpClient = w.HttpClient }

// WithClaims sets additional claims to request for the token, such as those required by conditional access policies.
// Use this option when Azure AD returned a claims challenge for a prior request. The argument must be decoded.
func WithClaims(claims string) AcquireTokenOption {
return withClaimsOption{Claims: claims}
}

// WithHTTPClient allows for a custom HTTP client to be set.
func WithHTTPClient(httpClient ops.HTTPClient) Option {
return withHTTPClientOption{HttpClient: httpClient}
}

// Client to be used to acquire tokens for managed identity.
// ID: [SystemAssigned()], [ClientID("clientID")], [ResourceID("resourceID")], [ObjectID("objectID")]
//
// Options: [WithHTTPClient]
func New(id ID, options ...Option) (Client, error) {
fmt.Println("idType: ", id.value())

opts := clientOptions{
claims: "claims",
}

for _, option := range options {
option.apply(&opts)
}

authInfo, err := authority.NewInfoFromAuthorityURI("authorityURI", true, false)
if err != nil {
return Client{}, err
}

authParams := authority.NewAuthParams(id.value(), authInfo)
client := Client{ // Note: Hey, don't even THINK about making Base into *Base. See "design notes" in public.go and confidential.go
AuthParams: authParams,
cacheAccessorMu: &sync.RWMutex{},
// manager: storage.New(token),
// pmanager: storage.NewPartitionedManager(token),
}

return client, err
}

// Acquires tokens from the configured managed identity on an azure resource.
//
// Resource: scopes application is requesting access to
// Options: [WithClaims]
func (client Client) AcquireToken(context context.Context, resource string, options ...AcquireTokenOption) (base.AuthResult, error) {
return base.AuthResult{}, nil
}

// Detects and returns the managed identity source available on the environment.
func GetSource() Source {
return DefaultToIMDS
}
46 changes: 46 additions & 0 deletions apps/managedidentity/managedidentity_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
package managedidentity

import (
"context"
"testing"
)

func fakeClient(mangedIdentityId ID, options ...Option) (Client, error) {
client, err := New(mangedIdentityId, options...)

if err != nil {
return Client{}, err
}

return client, nil
}

func TestManagedIdentity(t *testing.T) {
client, err := fakeClient(SystemAssigned())

if err != nil {
t.Fatal(err)
}

_, err = client.AcquireToken(context.Background(), "scope", WithClaims("claim"))

if err == nil {
bgavrilMS marked this conversation as resolved.
Show resolved Hide resolved
t.Errorf("TestManagedIdentity: unexpected nil error from TestManagedIdentity")
}
}

func TestManagedIdentityWithClaims(t *testing.T) {
client, err := fakeClient(ClientID("123"))

if err != nil {
t.Fatal(err)
}

_, err = client.AcquireToken(context.Background(), "scope", WithClaims("claim"))

if err == nil {
t.Errorf("TestManagedIdentityWithClaims: unexpected nil error from TestManagedIdentityWithClaims")
}
}
4 changes: 3 additions & 1 deletion apps/tests/devapps/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ func main() {
acquireTokenClientCertificate()

// this time the token comes from the cache!
acquireTokenClientCertificate()
// acquireTokenClientCertificate()
} else if exampleType == "7" {
RunManagedIdentity()
}
}
38 changes: 38 additions & 0 deletions apps/tests/devapps/managedidentity_sample.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package main

import (
"context"
"fmt"
"net/http"

mi "github.com/AzureAD/microsoft-authentication-library-for-go/apps/managedidentity"
)

func RunManagedIdentity() {
customHttpClient := &http.Client{}

miSystemAssigned, error := mi.New(mi.SystemAssigned())
if error != nil {
fmt.Println(error)
}

miClientIdAssigned, error := mi.New(mi.ClientID("client id 123"), mi.WithHTTPClient(customHttpClient))
if error != nil {
fmt.Println(error)
}

miResourceIdAssigned, error := mi.New(mi.ResourceID("resource id 123"))
if error != nil {
fmt.Println(error)
}

miObjectIdAssigned, error := mi.New(mi.ObjectID("object id 123"))
if error != nil {
fmt.Println(error)
}

miSystemAssigned.AcquireToken(context.Background(), "resource", mi.WithClaims("claim"))
miClientIdAssigned.AcquireToken(context.Background(), "resource")
miResourceIdAssigned.AcquireToken(context.Background(), "resource", mi.WithClaims("claim"))
miObjectIdAssigned.AcquireToken(context.Background(), "resource")
}