github.com/mdaxf/iac@v0.0.0-20240519030858-58a061660378/vendor_skip/go.mongodb.org/mongo-driver/x/mongo/driver/auth/scram.go (about)

     1  // Copyright (C) MongoDB, Inc. 2017-present.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"); you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
     6  
     7  // Copyright (C) MongoDB, Inc. 2018-present.
     8  //
     9  // Licensed under the Apache License, Version 2.0 (the "License"); you may
    10  // not use this file except in compliance with the License. You may obtain
    11  // a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  package auth
    14  
    15  import (
    16  	"context"
    17  	"fmt"
    18  
    19  	"github.com/xdg-go/scram"
    20  	"github.com/xdg-go/stringprep"
    21  	"go.mongodb.org/mongo-driver/x/bsonx/bsoncore"
    22  )
    23  
    24  const (
    25  	// SCRAMSHA1 holds the mechanism name "SCRAM-SHA-1"
    26  	SCRAMSHA1 = "SCRAM-SHA-1"
    27  
    28  	// SCRAMSHA256 holds the mechanism name "SCRAM-SHA-256"
    29  	SCRAMSHA256 = "SCRAM-SHA-256"
    30  )
    31  
    32  var (
    33  	// Additional options for the saslStart command to enable a shorter SCRAM conversation
    34  	scramStartOptions bsoncore.Document = bsoncore.BuildDocumentFromElements(nil,
    35  		bsoncore.AppendBooleanElement(nil, "skipEmptyExchange", true),
    36  	)
    37  )
    38  
    39  func newScramSHA1Authenticator(cred *Cred) (Authenticator, error) {
    40  	passdigest := mongoPasswordDigest(cred.Username, cred.Password)
    41  	client, err := scram.SHA1.NewClientUnprepped(cred.Username, passdigest, "")
    42  	if err != nil {
    43  		return nil, newAuthError("error initializing SCRAM-SHA-1 client", err)
    44  	}
    45  	client.WithMinIterations(4096)
    46  	return &ScramAuthenticator{
    47  		mechanism: SCRAMSHA1,
    48  		source:    cred.Source,
    49  		client:    client,
    50  	}, nil
    51  }
    52  
    53  func newScramSHA256Authenticator(cred *Cred) (Authenticator, error) {
    54  	passprep, err := stringprep.SASLprep.Prepare(cred.Password)
    55  	if err != nil {
    56  		return nil, newAuthError(fmt.Sprintf("error SASLprepping password '%s'", cred.Password), err)
    57  	}
    58  	client, err := scram.SHA256.NewClientUnprepped(cred.Username, passprep, "")
    59  	if err != nil {
    60  		return nil, newAuthError("error initializing SCRAM-SHA-256 client", err)
    61  	}
    62  	client.WithMinIterations(4096)
    63  	return &ScramAuthenticator{
    64  		mechanism: SCRAMSHA256,
    65  		source:    cred.Source,
    66  		client:    client,
    67  	}, nil
    68  }
    69  
    70  // ScramAuthenticator uses the SCRAM algorithm over SASL to authenticate a connection.
    71  type ScramAuthenticator struct {
    72  	mechanism string
    73  	source    string
    74  	client    *scram.Client
    75  }
    76  
    77  var _ SpeculativeAuthenticator = (*ScramAuthenticator)(nil)
    78  
    79  // Auth authenticates the provided connection by conducting a full SASL conversation.
    80  func (a *ScramAuthenticator) Auth(ctx context.Context, cfg *Config) error {
    81  	err := ConductSaslConversation(ctx, cfg, a.source, a.createSaslClient())
    82  	if err != nil {
    83  		return newAuthError("sasl conversation error", err)
    84  	}
    85  	return nil
    86  }
    87  
    88  // CreateSpeculativeConversation creates a speculative conversation for SCRAM authentication.
    89  func (a *ScramAuthenticator) CreateSpeculativeConversation() (SpeculativeConversation, error) {
    90  	return newSaslConversation(a.createSaslClient(), a.source, true), nil
    91  }
    92  
    93  func (a *ScramAuthenticator) createSaslClient() SaslClient {
    94  	return &scramSaslAdapter{
    95  		conversation: a.client.NewConversation(),
    96  		mechanism:    a.mechanism,
    97  	}
    98  }
    99  
   100  type scramSaslAdapter struct {
   101  	mechanism    string
   102  	conversation *scram.ClientConversation
   103  }
   104  
   105  var _ SaslClient = (*scramSaslAdapter)(nil)
   106  var _ ExtraOptionsSaslClient = (*scramSaslAdapter)(nil)
   107  
   108  func (a *scramSaslAdapter) Start() (string, []byte, error) {
   109  	step, err := a.conversation.Step("")
   110  	if err != nil {
   111  		return a.mechanism, nil, err
   112  	}
   113  	return a.mechanism, []byte(step), nil
   114  }
   115  
   116  func (a *scramSaslAdapter) Next(challenge []byte) ([]byte, error) {
   117  	step, err := a.conversation.Step(string(challenge))
   118  	if err != nil {
   119  		return nil, err
   120  	}
   121  	return []byte(step), nil
   122  }
   123  
   124  func (a *scramSaslAdapter) Completed() bool {
   125  	return a.conversation.Done()
   126  }
   127  
   128  func (*scramSaslAdapter) StartCommandOptions() bsoncore.Document {
   129  	return scramStartOptions
   130  }