Skip to content

Commit fb93f9c

Browse files
committed
api: add pap-sha256 auth support
The authentication method works only with Tarantool EE [1]. 1. tarantool/enterprise_doc#206 Closes #243
1 parent d72a10c commit fb93f9c

13 files changed

+244
-70
lines changed

.github/workflows/testing.yml

+8-5
Original file line numberDiff line numberDiff line change
@@ -116,13 +116,16 @@ jobs:
116116
fail-fast: false
117117
matrix:
118118
sdk-version:
119-
- '1.10.11-0-gf0b0e7ecf-r470'
120-
- '2.8.3-21-g7d35cd2be-r470'
119+
- 'bundle-1.10.11-0-gf0b0e7ecf-r470'
121120
coveralls: [false]
122121
fuzzing: [false]
123122
ssl: [false]
124123
include:
125-
- sdk-version: '2.10.0-1-gfa775b383-r486-linux-x86_64'
124+
- sdk-version: 'bundle-2.10.0-1-gfa775b383-r486-linux-x86_64'
125+
coveralls: false
126+
ssl: true
127+
- sdk-path: 'dev/linux/x86_64/master/'
128+
sdk-version: 'sdk-gc64-2.11.0-entrypoint-113-g803baaffe-r529.linux.x86_64'
126129
coveralls: true
127130
ssl: true
128131

@@ -141,8 +144,8 @@ jobs:
141144

142145
- name: Setup Tarantool ${{ matrix.sdk-version }}
143146
run: |
144-
ARCHIVE_NAME=tarantool-enterprise-bundle-${{ matrix.sdk-version }}.tar.gz
145-
curl -O -L https://${{ secrets.SDK_DOWNLOAD_TOKEN }}@download.tarantool.io/enterprise/${ARCHIVE_NAME}
147+
ARCHIVE_NAME=tarantool-enterprise-${{ matrix.sdk-version }}.tar.gz
148+
curl -O -L https://${{ secrets.SDK_DOWNLOAD_TOKEN }}@download.tarantool.io/enterprise/${{ matrix.sdk-path }}${ARCHIVE_NAME}
146149
tar -xzf ${ARCHIVE_NAME}
147150
rm -f ${ARCHIVE_NAME}
148151

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1515
- Error type support in MessagePack (#209)
1616
- Event subscription support (#119)
1717
- Session settings support (#215)
18+
- pap-sha256 authorization method support (Tarantool EE feature) (#243)
1819

1920
### Changed
2021

auth.go

+38
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,46 @@ package tarantool
33
import (
44
"crypto/sha1"
55
"encoding/base64"
6+
"fmt"
67
)
78

9+
const (
10+
chapSha1 = "chap-sha1"
11+
papSha256 = "pap-sha256"
12+
)
13+
14+
// Auth is used as a parameter to set up an authentication method.
15+
type Auth int
16+
17+
const (
18+
// AutoAuth does not force any authentication method. A method will be
19+
// selected automatically (a value from IPROTO_ID response or
20+
// ChapSha1Auth).
21+
AutoAuth Auth = iota
22+
// ChapSha1Auth forces chap-sha1 authentication method. The method is
23+
// available both in the Tarantool Community Edition (CE) and the
24+
// Tarantool Enterprise Edition (EE)
25+
ChapSha1Auth
26+
// PapSha256Auth forces pap-sha256 authentication method. The method is
27+
// available only for the Tarantool Enterprise Edition (EE) with
28+
// SSL transport.
29+
PapSha256Auth
30+
)
31+
32+
// String returns a string representation of an authentication method.
33+
func (a Auth) String() string {
34+
switch a {
35+
case AutoAuth:
36+
return "auto"
37+
case ChapSha1Auth:
38+
return chapSha1
39+
case PapSha256Auth:
40+
return papSha256
41+
default:
42+
return fmt.Sprintf("unknown auth type (code %d)", a)
43+
}
44+
}
45+
846
func scramble(encodedSalt, pass string) (scramble []byte, err error) {
947
/* ==================================================================
1048
According to: http://tarantool.org/doc/dev_guide/box-protocol.html

auth_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package tarantool_test
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
7+
"github.com/stretchr/testify/assert"
8+
. "github.com/tarantool/go-tarantool"
9+
)
10+
11+
func TestAuth_String(t *testing.T) {
12+
unknownId := int(PapSha256Auth) + 1
13+
tests := []struct {
14+
auth Auth
15+
expected string
16+
}{
17+
{AutoAuth, "auto"},
18+
{ChapSha1Auth, "chap-sha1"},
19+
{PapSha256Auth, "pap-sha256"},
20+
{Auth(unknownId), fmt.Sprintf("unknown auth type (code %d)", unknownId)},
21+
}
22+
23+
for _, tc := range tests {
24+
t.Run(tc.expected, func(t *testing.T) {
25+
assert.Equal(t, tc.auth.String(), tc.expected)
26+
})
27+
}
28+
}

config.lua

+7
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
-- Do not set listen for now so connector won't be
22
-- able to send requests until everything is configured.
3+
local auth_type = os.getenv("TEST_TNT_AUTH_TYPE")
4+
if auth_type == "auto" then
5+
auth_type = nil
6+
end
7+
38
box.cfg{
9+
auth_type = auth_type,
410
work_dir = os.getenv("TEST_TNT_WORK_DIR"),
511
memtx_use_mvcc_engine = os.getenv("TEST_TNT_MEMTX_USE_MVCC_ENGINE") == 'true' or nil,
612
}
@@ -267,5 +273,6 @@ box.space.test:truncate()
267273

268274
-- Set listen only when every other thing is configured.
269275
box.cfg{
276+
auth_type = auth_type,
270277
listen = os.getenv("TEST_TNT_LISTEN"),
271278
}

connection.go

+39-53
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,8 @@ type Greeting struct {
226226

227227
// Opts is a way to configure Connection
228228
type Opts struct {
229+
// Auth is an authentication method.
230+
Auth Auth
229231
// Timeout for response to a particular request. The timeout is reset when
230232
// push messages are received. If Timeout is zero, any request can be
231233
// blocked infinitely.
@@ -546,19 +548,40 @@ func (conn *Connection) dial() (err error) {
546548

547549
// Auth.
548550
if opts.User != "" {
549-
scr, err := scramble(conn.Greeting.auth, opts.Pass)
550-
if err != nil {
551-
err = errors.New("auth: scrambling failure " + err.Error())
551+
auth := opts.Auth
552+
if opts.Auth == AutoAuth {
553+
if conn.serverProtocolInfo.Auth != AutoAuth {
554+
auth = conn.serverProtocolInfo.Auth
555+
} else {
556+
auth = ChapSha1Auth
557+
}
558+
}
559+
560+
var req Request
561+
if auth == ChapSha1Auth {
562+
salt := conn.Greeting.auth
563+
req, err = newChapSha1AuthRequest(conn.opts.User, salt, opts.Pass)
564+
if err != nil {
565+
return fmt.Errorf("auth: %w", err)
566+
}
567+
} else if auth == PapSha256Auth {
568+
if opts.Transport != connTransportSsl {
569+
return errors.New("auth: forbidden to use " + auth.String() +
570+
" unless SSL is enabled for the connection")
571+
}
572+
req = newPapSha256AuthRequest(conn.opts.User, opts.Pass)
573+
} else {
552574
connection.Close()
553-
return err
575+
return errors.New("auth: " + auth.String())
554576
}
555-
if err = conn.writeAuthRequest(w, scr); err != nil {
577+
578+
if err = conn.writeRequest(w, req); err != nil {
556579
connection.Close()
557-
return err
580+
return fmt.Errorf("auth: %w", err)
558581
}
559-
if err = conn.readAuthResponse(r); err != nil {
582+
if _, err = conn.readResponse(r); err != nil {
560583
connection.Close()
561-
return err
584+
return fmt.Errorf("auth: %w", err)
562585
}
563586
}
564587

@@ -662,28 +685,6 @@ func (conn *Connection) writeRequest(w *bufio.Writer, req Request) error {
662685
return err
663686
}
664687

665-
func (conn *Connection) writeAuthRequest(w *bufio.Writer, scramble []byte) error {
666-
req := newAuthRequest(conn.opts.User, string(scramble))
667-
668-
err := conn.writeRequest(w, req)
669-
if err != nil {
670-
return fmt.Errorf("auth: %w", err)
671-
}
672-
673-
return nil
674-
}
675-
676-
func (conn *Connection) writeIdRequest(w *bufio.Writer, protocolInfo ProtocolInfo) error {
677-
req := NewIdRequest(protocolInfo)
678-
679-
err := conn.writeRequest(w, req)
680-
if err != nil {
681-
return fmt.Errorf("identify: %w", err)
682-
}
683-
684-
return nil
685-
}
686-
687688
func (conn *Connection) readResponse(r io.Reader) (Response, error) {
688689
respBytes, err := conn.read(r)
689690
if err != nil {
@@ -707,24 +708,6 @@ func (conn *Connection) readResponse(r io.Reader) (Response, error) {
707708
return resp, nil
708709
}
709710

710-
func (conn *Connection) readAuthResponse(r io.Reader) error {
711-
_, err := conn.readResponse(r)
712-
if err != nil {
713-
return fmt.Errorf("auth: %w", err)
714-
}
715-
716-
return nil
717-
}
718-
719-
func (conn *Connection) readIdResponse(r io.Reader) (Response, error) {
720-
resp, err := conn.readResponse(r)
721-
if err != nil {
722-
return resp, fmt.Errorf("identify: %w", err)
723-
}
724-
725-
return resp, nil
726-
}
727-
728711
func (conn *Connection) createConnection(reconnect bool) (err error) {
729712
var reconnects uint
730713
for conn.c == nil && conn.state == connDisconnected {
@@ -1625,19 +1608,20 @@ func checkProtocolInfo(expected ProtocolInfo, actual ProtocolInfo) error {
16251608
func (conn *Connection) identify(w *bufio.Writer, r *bufio.Reader) error {
16261609
var ok bool
16271610

1628-
werr := conn.writeIdRequest(w, clientProtocolInfo)
1611+
req := NewIdRequest(clientProtocolInfo)
1612+
werr := conn.writeRequest(w, req)
16291613
if werr != nil {
1630-
return werr
1614+
return fmt.Errorf("identify: %w", werr)
16311615
}
16321616

1633-
resp, rerr := conn.readIdResponse(r)
1617+
resp, rerr := conn.readResponse(r)
16341618
if rerr != nil {
16351619
if resp.Code == ErrUnknownRequestType {
16361620
// IPROTO_ID requests are not supported by server.
16371621
return nil
16381622
}
16391623

1640-
return rerr
1624+
return fmt.Errorf("identify: %w", rerr)
16411625
}
16421626

16431627
if len(resp.Data) == 0 {
@@ -1664,5 +1648,7 @@ func (conn *Connection) ServerProtocolInfo() ProtocolInfo {
16641648
// supported by Go connection client.
16651649
// Since 1.10.0
16661650
func (conn *Connection) ClientProtocolInfo() ProtocolInfo {
1667-
return clientProtocolInfo.Clone()
1651+
info := clientProtocolInfo.Clone()
1652+
info.Auth = conn.opts.Auth
1653+
return info
16681654
}

const.go

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ const (
5151
KeyEvent = 0x57
5252
KeyEventData = 0x58
5353
KeyTxnIsolation = 0x59
54+
KeyAuthType = 0x5b
5455

5556
KeyFieldName = 0x00
5657
KeyFieldType = 0x01

protocol.go

+2
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ type ProtocolFeature uint64
1313

1414
// ProtocolInfo type aggregates Tarantool protocol version and features info.
1515
type ProtocolInfo struct {
16+
// Auth is an authentication method.
17+
Auth Auth
1618
// Version is the supported protocol version.
1719
Version ProtocolVersion
1820
// Features are supported protocol features.

request.go

+21-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package tarantool
33
import (
44
"context"
55
"errors"
6+
"fmt"
67
"reflect"
78
"strings"
89
"sync"
@@ -591,22 +592,38 @@ func (req *spaceIndexRequest) setIndex(index interface{}) {
591592

592593
type authRequest struct {
593594
baseRequest
594-
user, scramble string
595+
auth Auth
596+
user, pass string
595597
}
596598

597-
func newAuthRequest(user, scramble string) *authRequest {
599+
func newChapSha1AuthRequest(user, salt, password string) (*authRequest, error) {
600+
scr, err := scramble(salt, password)
601+
if err != nil {
602+
return nil, fmt.Errorf("scrambling failure: %w", err)
603+
}
604+
605+
req := new(authRequest)
606+
req.requestCode = AuthRequestCode
607+
req.auth = ChapSha1Auth
608+
req.user = user
609+
req.pass = string(scr)
610+
return req, nil
611+
}
612+
613+
func newPapSha256AuthRequest(user, password string) *authRequest {
598614
req := new(authRequest)
599615
req.requestCode = AuthRequestCode
616+
req.auth = PapSha256Auth
600617
req.user = user
601-
req.scramble = scramble
618+
req.pass = password
602619
return req
603620
}
604621

605622
// Body fills an encoder with the auth request body.
606623
func (req *authRequest) Body(res SchemaResolver, enc *encoder) error {
607624
return enc.Encode(map[uint32]interface{}{
608625
KeyUserName: req.user,
609-
KeyTuple: []interface{}{string("chap-sha1"), string(req.scramble)},
626+
KeyTuple: []interface{}{req.auth.String(), req.pass},
610627
})
611628
}
612629

response.go

+15
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,21 @@ func (resp *Response) decodeBody() (err error) {
213213
}
214214
serverProtocolInfo.Features[i] = feature
215215
}
216+
case KeyAuthType:
217+
var auth string
218+
if auth, err = d.DecodeString(); err != nil {
219+
return err
220+
}
221+
found := false
222+
for _, a := range [...]Auth{ChapSha1Auth, PapSha256Auth} {
223+
if auth == a.String() {
224+
serverProtocolInfo.Auth = a
225+
found = true
226+
}
227+
}
228+
if !found {
229+
return fmt.Errorf("unknown auth type %s", auth)
230+
}
216231
default:
217232
if err = d.Skip(); err != nil {
218233
return err

0 commit comments

Comments
 (0)