github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/ldap.v2/passwdmodify.go (about)

     1  // This file contains the password modify extended operation as specified in rfc 3062
     2  //
     3  // https://tools.ietf.org/html/rfc3062
     4  //
     5  
     6  package ldap
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  
    12  	"gopkg.in/asn1-ber.v1"
    13  )
    14  
    15  const (
    16  	passwordModifyOID = "1.3.6.1.4.1.4203.1.11.1"
    17  )
    18  
    19  // PasswordModifyRequest implements the Password Modify Extended Operation as defined in https://www.ietf.org/rfc/rfc3062.txt
    20  type PasswordModifyRequest struct {
    21  	// UserIdentity is an optional string representation of the user associated with the request.
    22  	// This string may or may not be an LDAPDN [RFC2253].
    23  	// If no UserIdentity field is present, the request acts up upon the password of the user currently associated with the LDAP session
    24  	UserIdentity string
    25  	// OldPassword, if present, contains the user's current password
    26  	OldPassword string
    27  	// NewPassword, if present, contains the desired password for this user
    28  	NewPassword string
    29  }
    30  
    31  // PasswordModifyResult holds the server response to a PasswordModifyRequest
    32  type PasswordModifyResult struct {
    33  	// GeneratedPassword holds a password generated by the server, if present
    34  	GeneratedPassword string
    35  }
    36  
    37  func (r *PasswordModifyRequest) encode() (*ber.Packet, error) {
    38  	request := ber.Encode(ber.ClassApplication, ber.TypeConstructed, ApplicationExtendedRequest, nil, "Password Modify Extended Operation")
    39  	request.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, passwordModifyOID, "Extended Request Name: Password Modify OID"))
    40  	extendedRequestValue := ber.Encode(ber.ClassContext, ber.TypePrimitive, 1, nil, "Extended Request Value: Password Modify Request")
    41  	passwordModifyRequestValue := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "Password Modify Request")
    42  	if r.UserIdentity != "" {
    43  		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 0, r.UserIdentity, "User Identity"))
    44  	}
    45  	if r.OldPassword != "" {
    46  		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 1, r.OldPassword, "Old Password"))
    47  	}
    48  	if r.NewPassword != "" {
    49  		passwordModifyRequestValue.AppendChild(ber.NewString(ber.ClassContext, ber.TypePrimitive, 2, r.NewPassword, "New Password"))
    50  	}
    51  
    52  	extendedRequestValue.AppendChild(passwordModifyRequestValue)
    53  	request.AppendChild(extendedRequestValue)
    54  
    55  	return request, nil
    56  }
    57  
    58  // NewPasswordModifyRequest creates a new PasswordModifyRequest
    59  //
    60  // According to the RFC 3602:
    61  // userIdentity is a string representing the user associated with the request.
    62  // This string may or may not be an LDAPDN (RFC 2253).
    63  // If userIdentity is empty then the operation will act on the user associated
    64  // with the session.
    65  //
    66  // oldPassword is the current user's password, it can be empty or it can be
    67  // needed depending on the session user access rights (usually an administrator
    68  // can change a user's password without knowing the current one) and the
    69  // password policy (see pwdSafeModify password policy's attribute)
    70  //
    71  // newPassword is the desired user's password. If empty the server can return
    72  // an error or generate a new password that will be available in the
    73  // PasswordModifyResult.GeneratedPassword
    74  //
    75  func NewPasswordModifyRequest(userIdentity string, oldPassword string, newPassword string) *PasswordModifyRequest {
    76  	return &PasswordModifyRequest{
    77  		UserIdentity: userIdentity,
    78  		OldPassword:  oldPassword,
    79  		NewPassword:  newPassword,
    80  	}
    81  }
    82  
    83  // PasswordModify performs the modification request
    84  func (l *Conn) PasswordModify(passwordModifyRequest *PasswordModifyRequest) (*PasswordModifyResult, error) {
    85  	packet := ber.Encode(ber.ClassUniversal, ber.TypeConstructed, ber.TagSequence, nil, "LDAP Request")
    86  	packet.AppendChild(ber.NewInteger(ber.ClassUniversal, ber.TypePrimitive, ber.TagInteger, l.nextMessageID(), "MessageID"))
    87  
    88  	encodedPasswordModifyRequest, err := passwordModifyRequest.encode()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  	packet.AppendChild(encodedPasswordModifyRequest)
    93  
    94  	l.Debug.PrintPacket(packet)
    95  
    96  	msgCtx, err := l.sendMessage(packet)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  	defer l.finishMessage(msgCtx)
   101  
   102  	result := &PasswordModifyResult{}
   103  
   104  	l.Debug.Printf("%d: waiting for response", msgCtx.id)
   105  	packetResponse, ok := <-msgCtx.responses
   106  	if !ok {
   107  		return nil, NewError(ErrorNetwork, errors.New("ldap: response channel closed"))
   108  	}
   109  	packet, err = packetResponse.ReadPacket()
   110  	l.Debug.Printf("%d: got response %p", msgCtx.id, packet)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	if packet == nil {
   116  		return nil, NewError(ErrorNetwork, errors.New("ldap: could not retrieve message"))
   117  	}
   118  
   119  	if l.Debug {
   120  		if err := addLDAPDescriptions(packet); err != nil {
   121  			return nil, err
   122  		}
   123  		ber.PrintPacket(packet)
   124  	}
   125  
   126  	if packet.Children[1].Tag == ApplicationExtendedResponse {
   127  		resultCode, resultDescription := getLDAPResultCode(packet)
   128  		if resultCode != 0 {
   129  			return nil, NewError(resultCode, errors.New(resultDescription))
   130  		}
   131  	} else {
   132  		return nil, NewError(ErrorUnexpectedResponse, fmt.Errorf("Unexpected Response: %d", packet.Children[1].Tag))
   133  	}
   134  
   135  	extendedResponse := packet.Children[1]
   136  	for _, child := range extendedResponse.Children {
   137  		if child.Tag == 11 {
   138  			passwordModifyReponseValue := ber.DecodePacket(child.Data.Bytes())
   139  			if len(passwordModifyReponseValue.Children) == 1 {
   140  				if passwordModifyReponseValue.Children[0].Tag == 0 {
   141  					result.GeneratedPassword = ber.DecodeString(passwordModifyReponseValue.Children[0].Data.Bytes())
   142  				}
   143  			}
   144  		}
   145  	}
   146  
   147  	return result, nil
   148  }