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

GODRIVER-2758: Add documentation examples [master] #1801

Merged
merged 3 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ repos:
rev: "v2.2.6"
hooks:
- id: codespell
args: ["-L", "te,fo,fle,alo,nin,compres,wil,collone,asess,sav,ot,wll,dne,nulll,hellow"]
exclude: ^(vendor/|internal/cmd/benchmark/operation_test.go|bson/testdata/lorem.txt)
args: ["-L", "te,fo,fle,alo,nin,compres,wil,collone,asess,sav,ot,wll,dne,nulll,hellow,aks"]
exclude: ^(vendor/|internal/cmd/benchmark/operation_test.go|bson/testdata/)
exclude_types: [json,yaml,pem]

- repo: https://github.com/tcort/markdown-link-check
Expand Down
265 changes: 265 additions & 0 deletions mongo/client_examples_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"context"
"fmt"
"log"
"os"

"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
Expand Down Expand Up @@ -459,3 +460,267 @@ func ExampleConnect_bSONOptions() {
panic(err)
}
}

func ExampleConnect_oIDC() {
// The `MONGODB-OIDC authentication mechanism` is available in MongoDB 7.0+
// on Linux platforms.
//
// The MONGODB-OIDC mechanism authenticates using an OpenID Connect (OIDC)
// access token. The driver supports OIDC for workload identity, defined as
// an identity you assign to a software workload (such as an application,
// service, script, or container) to authenticate and access other services
// and resources.
//
// The driver also supports OIDC for workforce identity for a more secure
// flow with a human in the loop.

// Credentials can be configured through the MongoDB URI or as arguments in
// the options.ClientOptions struct that is passed into the mongo.Connect
// function.

// Built-in Support
// The driver has built-in support for Azure IMDS and GCP
// IMDS environments. Other environments are supported with `Custom
// Callbacks`.

// Azure IMDS
// For an application running on an Azure VM or otherwise using the `Azure
// Internal Metadata Service`, you can use the built-in support for Azure,
// where "<client_id>" below is the client id of the Azure managed identity,
// and ``<audience>`` is the url-encoded ``audience`` `configured on your
// MongoDB deployment`.
{
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "azure",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
Username: "<client_id>",
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
},
)
c, err := mongo.Connect(opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// If the application is running on an Azure VM and only one managed
// identity is associated with the VM, "username" can be omitted.

// GCP IMDS

// For an application running on an GCP VM or otherwise using the `GCP
// Internal Metadata Service`_, you can use the built-in support for GCP,
// where "<audience>" below is the url-encoded "audience" `configured on
// your MongoDB deployment`.
{
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
},
)
c, err := mongo.Connect(opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// Custom Callbacks

// For environments that are not directly supported by the driver, you can
// use options.OIDCCallback. Some examples are given below.

// AWS EKS

// For an EKS Cluster with a configured `IAM OIDC provider`, the token can
// be read from a path given by the "AWS_WEB_IDENTITY_TOKEN_FILE"
// environment variable.
{
eksCallback := func(_ context.Context,
_ *options.OIDCArgs) (*options.OIDCCredential, error) {
accessToken, err := os.ReadFile(
os.Getenv("AWS_WEB_IDENTITY_TOKEN_FILE"))
if err != nil {
return nil, err
}
return &options.OIDCCredential{
AccessToken: string(accessToken),
}, nil
}
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
OIDCMachineCallback: eksCallback,
},
)
c, err := mongo.Connect(opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// Other Azure Environments

// For applications running on Azure Functions, App Service Environment
// (ASE), or Azure Kubernetes Service (AKS), you can use the `azidentity
// package`
// (https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) to
// fetch the credentials. In each case, the OIDCCallback function should
// return the AccessToken from the azidentity package.

// GCP GKE

// For a Google Kubernetes Engine cluster with a `configured service
// account`, the token can be read from the standard service account token
// file location.
{
gkeCallback := func(_ context.Context,
_ *options.OIDCArgs) (*options.OIDCCredential, error) {
accessToken, err := os.ReadFile(
"/var/run/secrets/kubernetes.io/serviceaccount/token")
if err != nil {
return nil, err
}
return &options.OIDCCredential{
AccessToken: string(accessToken),
}, nil
}
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
OIDCMachineCallback: gkeCallback,
},
)
c, err := mongo.Connect(opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// For workforce identity, the Client must be configured with the
// OIDCHumanCallback rather than the OIDCMachineCallback. The
// OIDCHumanCallback is used by the driver in a process that is two step. In
// the first step, the driver retrieves the Identity Provider (IDP)
// Information (IDPInfo) for the passed username. The OIDCHumanCallback then
// needs to negotiate with the IDP in order to obtain an AccessToken,
// possible RefreshToken, any timeouts, and return them, similar to the
// OIDCMachineCallbacks seen above. See
// https://docs.hidglobal.com/dev/auth-service/integration/openid-authentication-flows.html
// for more information on various OIDC authentication flows.
{
humanCallback := func(ctx context.Context,
opts *options.OIDCArgs) (*options.OIDCCredential, error) {
// idpInfo passed from the driver by asking the MongoDB server for
// the info configured for the username
idpInfo := opts.IDPInfo
// negotiateWithIDP must work with the IdP to obtain an access
// token. In many cases this will involve opening a webbrowser or
// providing a URL on the command line to a human-in-the-loop who
// can give permissions to the IdP.
accessToken, err := negotiateWithIDP(ctx, idpInfo.Issuer)
if err != nil {
return nil, err
}
return &options.OIDCCredential{
AccessToken: accessToken,
}, nil
}
uri := os.Getenv("MONGODB_URI")
props := map[string]string{
"ENVIRONMENT": "gcp",
"TOKEN_RESOURCE": "<audience>",
}
opts := options.Client().ApplyURI(uri)
opts.SetAuth(
options.Credential{
AuthMechanism: "MONGODB-OIDC",
AuthMechanismProperties: props,
OIDCHumanCallback: humanCallback,
},
)
c, err := mongo.Connect(opts)
if err != nil {
panic(err)
}
defer func() { _ = c.Disconnect(context.TODO()) }()
_, err = c.Database("test").
Collection("test").
InsertOne(context.TODO(), bson.D{})
if err != nil {
panic(err)
}
}

// * MONGODB-OIDC authentication mechanism:
// https://www.mongodb.com/docs/manual/core/security-oidc/
// * OIDC Identity Provider Configuration:
// https://www.mongodb.com/docs/manual/reference/parameters/#mongodb-parameter-param.oidcIdentityProviders
// * Azure Internal Metadata Service:
// https://learn.microsoft.com/en-us/azure/virtual-machines/instance-metadata-service
// * GCP Internal Metadata Service:
// https://cloud.google.com/compute/docs/metadata/querying-metadata
// * IAM OIDC provider:
// https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html
// * azure-identity package:
// https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity
// * configured service account:
// https://cloud.google.com/kubernetes-engine/docs/how-to/service-accounts
}

func negotiateWithIDP(_ context.Context, _ string) (string, error) {
return "", nil
}
Loading