forked from couchbase/gocbcore
-
Notifications
You must be signed in to change notification settings - Fork 0
/
authclient.go
142 lines (117 loc) · 4.69 KB
/
authclient.go
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
package gocbcore
import (
"crypto/sha1" // nolint: gosec
"crypto/sha256"
"crypto/sha512"
"hash"
"time"
"github.com/couchbase/gocbcore/v10/memd"
scram "github.com/couchbase/gocbcore/v10/scram"
)
// AuthMechanism represents a type of auth that can be performed.
type AuthMechanism string
const (
// PlainAuthMechanism represents that PLAIN auth should be performed.
PlainAuthMechanism = AuthMechanism("PLAIN")
// ScramSha1AuthMechanism represents that SCRAM SHA1 auth should be performed.
ScramSha1AuthMechanism = AuthMechanism("SCRAM-SHA1")
// ScramSha256AuthMechanism represents that SCRAM SHA256 auth should be performed.
ScramSha256AuthMechanism = AuthMechanism("SCRAM-SHA256")
// ScramSha512AuthMechanism represents that SCRAM SHA512 auth should be performed.
ScramSha512AuthMechanism = AuthMechanism("SCRAM-SHA512")
)
// AuthClient exposes an interface for performing authentication on a
// connected Couchbase K/V client.
type AuthClient interface {
Address() string
SupportsFeature(feature memd.HelloFeature) bool
SaslListMechs(deadline time.Time, cb func(mechs []AuthMechanism, err error)) error
SaslAuth(k, v []byte, deadline time.Time, cb func(b []byte, err error)) error
SaslStep(k, v []byte, deadline time.Time, cb func(err error)) error
}
// SaslListMechsCompleted is used to contain the result and/or error from a SaslListMechs operation.
type SaslListMechsCompleted struct {
Err error
Mechs []AuthMechanism
}
// SaslAuthPlain performs PLAIN SASL authentication against an AuthClient.
func SaslAuthPlain(username, password string, client AuthClient, deadline time.Time, cb func(err error)) error {
// Build PLAIN auth data
userBuf := []byte(username)
passBuf := []byte(password)
authData := make([]byte, 1+len(userBuf)+1+len(passBuf))
authData[0] = 0
copy(authData[1:], userBuf)
authData[1+len(userBuf)] = 0
copy(authData[1+len(userBuf)+1:], passBuf)
// Execute PLAIN authentication
err := client.SaslAuth([]byte(PlainAuthMechanism), authData, deadline, func(b []byte, err error) {
if err != nil {
cb(err)
return
}
cb(nil)
})
if err != nil {
return err
}
return nil
}
func saslAuthScram(saslName []byte, newHash func() hash.Hash, username, password string, client AuthClient,
deadline time.Time, continueCb func(), completedCb func(err error)) error {
scramMgr := scram.NewClient(newHash, username, password)
// Perform the initial SASL step
scramMgr.Step(nil)
err := client.SaslAuth(saslName, scramMgr.Out(), deadline, func(b []byte, err error) {
if err != nil && !isErrorStatus(err, memd.StatusAuthContinue) {
completedCb(err)
return
}
if !scramMgr.Step(b) {
err = scramMgr.Err()
if err != nil {
completedCb(err)
return
}
logErrorf("Local auth client finished before server accepted auth")
completedCb(nil)
return
}
err = client.SaslStep(saslName, scramMgr.Out(), deadline, completedCb)
if err != nil {
completedCb(err)
return
}
continueCb()
})
if err != nil {
return err
}
return nil
}
// SaslAuthScramSha1 performs SCRAM-SHA1 SASL authentication against an AuthClient.
func SaslAuthScramSha1(username, password string, client AuthClient, deadline time.Time, continueCb func(), completedCb func(err error)) error {
return saslAuthScram([]byte("SCRAM-SHA1"), sha1.New, username, password, client, deadline, continueCb, completedCb)
}
// SaslAuthScramSha256 performs SCRAM-SHA256 SASL authentication against an AuthClient.
func SaslAuthScramSha256(username, password string, client AuthClient, deadline time.Time, continueCb func(), completedCb func(err error)) error {
return saslAuthScram([]byte("SCRAM-SHA256"), sha256.New, username, password, client, deadline, continueCb, completedCb)
}
// SaslAuthScramSha512 performs SCRAM-SHA512 SASL authentication against an AuthClient.
func SaslAuthScramSha512(username, password string, client AuthClient, deadline time.Time, continueCb func(), completedCb func(err error)) error {
return saslAuthScram([]byte("SCRAM-SHA512"), sha512.New, username, password, client, deadline, continueCb, completedCb)
}
func saslMethod(method AuthMechanism, username, password string, client AuthClient, deadline time.Time, continueCb func(), completedCb func(err error)) error {
switch method {
case PlainAuthMechanism:
return SaslAuthPlain(username, password, client, deadline, completedCb)
case ScramSha1AuthMechanism:
return SaslAuthScramSha1(username, password, client, deadline, continueCb, completedCb)
case ScramSha256AuthMechanism:
return SaslAuthScramSha256(username, password, client, deadline, continueCb, completedCb)
case ScramSha512AuthMechanism:
return SaslAuthScramSha512(username, password, client, deadline, continueCb, completedCb)
default:
return errNoSupportedMechanisms
}
}