Skip to content

Commit c7c8ce2

Browse files
authored
feat: added skip-hash option for basic-auth creation (#576)
* feat: added skip-hash option for basic-auth creation * tests: added unit tests for new methods * style: removed unnecessary declarations * chore: added changelog for basic-auth skip-hash change * style: introduced credentialOptions to make the credential interface cleaner * style: reusing CreateWithOptions and UpdateWithOptions methods
1 parent 616c2c9 commit c7c8ce2

11 files changed

+362
-25
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Table of Contents
22

3+
- [v0.68.0](#v0680)
34
- [v0.67.0](#v0670)
45
- [v0.66.1](#v0661)
56
- [v0.66.0](#v0660)
@@ -84,6 +85,15 @@
8485
- [0.2.0](#020)
8586
- [0.1.0](#010)
8687

88+
## [v0.68.0]
89+
90+
> Release date: 2025/09/26
91+
92+
- Added `BasicAuthOptions` to allow skipping hashing for
93+
BasicAuth passwords. `CreateWithOptions` and `UpdateWitOptions`
94+
methods are added for basic-auth for the same.
95+
[#576](https://github.com/Kong/go-kong/pull/576)
96+
8797
## [v0.67.0]
8898

8999
> Release date: 2025/05/26

kong/acl_service.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ func (s *ACLService) Create(ctx context.Context,
3737
consumerUsernameOrID *string, aclGroup *ACLGroup,
3838
) (*ACLGroup, error) {
3939
cred, err := s.client.credentials.Create(ctx, "acl",
40-
consumerUsernameOrID, aclGroup)
40+
consumerUsernameOrID, credentialOptions{
41+
credential: aclGroup,
42+
})
4143
if err != nil {
4244
return nil, err
4345
}
@@ -94,7 +96,9 @@ func (s *ACLService) Update(ctx context.Context,
9496
consumerUsernameOrID *string, aclGroup *ACLGroup,
9597
) (*ACLGroup, error) {
9698
cred, err := s.client.credentials.Update(ctx, "acl",
97-
consumerUsernameOrID, aclGroup)
99+
consumerUsernameOrID, credentialOptions{
100+
credential: aclGroup,
101+
})
98102
if err != nil {
99103
return nil, err
100104
}

kong/basic_auth_service.go

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,33 @@ package kong
33
import (
44
"context"
55
"encoding/json"
6+
"fmt"
67
)
78

9+
// BasicAuthOptions provides configuration options for basic auth operations
10+
// +k8s:deepcopy-gen=true
11+
type BasicAuthOptions struct {
12+
BasicAuth
13+
SkipHash *bool
14+
}
15+
816
// AbstractBasicAuthService handles basic-auth credentials in Kong.
917
type AbstractBasicAuthService interface {
1018
// Create creates a basic-auth credential in Kong
1119
// is auto-generated.
1220
Create(ctx context.Context, consumerUsernameOrID *string, basicAuth *BasicAuth) (*BasicAuth, error)
21+
// CreateWithOptions creates a basic-auth credential in Kong
22+
// with the options provided.
23+
CreateWithOptions(ctx context.Context, consumerUsernameOrID *string, opts *BasicAuthOptions) (*BasicAuth, error)
1324
// Get fetches a basic-auth credential from Kong.
1425
Get(ctx context.Context, consumerUsernameOrID, usernameOrID *string) (*BasicAuth, error)
1526
// GetByID fetches a basic-auth credential from Kong using ID.
1627
GetByID(ctx context.Context, id *string) (*BasicAuth, error)
1728
// Update updates a basic-auth credential in Kong
1829
Update(ctx context.Context, consumerUsernameOrID *string, basicAuth *BasicAuth) (*BasicAuth, error)
30+
// UpdateWithOptions updates a basic-auth credential in Kong
31+
// with the options provided.
32+
UpdateWithOptions(ctx context.Context, consumerUsernameOrID *string, opts *BasicAuthOptions) (*BasicAuth, error)
1933
// Delete deletes a basic-auth credential in Kong
2034
Delete(ctx context.Context, consumerUsernameOrID, usernameOrID *string) error
2135
// List fetches a list of basic-auth credentials in Kong.
@@ -37,8 +51,35 @@ type BasicAuthService service
3751
func (s *BasicAuthService) Create(ctx context.Context,
3852
consumerUsernameOrID *string, basicAuth *BasicAuth,
3953
) (*BasicAuth, error) {
54+
if basicAuth == nil {
55+
return nil, fmt.Errorf("basic auth credential is required")
56+
}
57+
58+
return s.CreateWithOptions(ctx, consumerUsernameOrID, &BasicAuthOptions{BasicAuth: *basicAuth, SkipHash: nil})
59+
}
60+
61+
// CreateWithOptions creates a basic-auth credential in Kong
62+
// with the options provided.
63+
// If an ID is specified, it will be used to
64+
// create a basic-auth in Kong, otherwise an ID
65+
// is auto-generated.
66+
func (s *BasicAuthService) CreateWithOptions(ctx context.Context,
67+
consumerUsernameOrID *string, opts *BasicAuthOptions,
68+
) (*BasicAuth, error) {
69+
if opts == nil {
70+
return nil, fmt.Errorf("basic auth options and credential are required")
71+
}
72+
73+
var skipHash bool
74+
if opts.SkipHash != nil {
75+
skipHash = *opts.SkipHash
76+
}
77+
4078
cred, err := s.client.credentials.Create(ctx, "basic-auth",
41-
consumerUsernameOrID, basicAuth)
79+
consumerUsernameOrID, credentialOptions{
80+
credential: &opts.BasicAuth,
81+
skipHash: skipHash,
82+
})
4283
if err != nil {
4384
return nil, err
4485
}
@@ -94,8 +135,32 @@ func (s *BasicAuthService) GetByID(ctx context.Context,
94135
func (s *BasicAuthService) Update(ctx context.Context,
95136
consumerUsernameOrID *string, basicAuth *BasicAuth,
96137
) (*BasicAuth, error) {
138+
if basicAuth == nil {
139+
return nil, fmt.Errorf("basic auth credential is required")
140+
}
141+
142+
return s.UpdateWithOptions(ctx, consumerUsernameOrID, &BasicAuthOptions{BasicAuth: *basicAuth, SkipHash: nil})
143+
}
144+
145+
// UpdateWithOptions updates a basic-auth credential in Kong
146+
// with the options provided.
147+
func (s *BasicAuthService) UpdateWithOptions(ctx context.Context,
148+
consumerUsernameOrID *string, opts *BasicAuthOptions,
149+
) (*BasicAuth, error) {
150+
if opts == nil {
151+
return nil, fmt.Errorf("basic auth options and credential are required")
152+
}
153+
154+
var skipHash bool
155+
if opts.SkipHash != nil {
156+
skipHash = *opts.SkipHash
157+
}
158+
97159
cred, err := s.client.credentials.Update(ctx, "basic-auth",
98-
consumerUsernameOrID, basicAuth)
160+
consumerUsernameOrID, credentialOptions{
161+
credential: &opts.BasicAuth,
162+
skipHash: skipHash,
163+
})
99164
if err != nil {
100165
return nil, err
101166
}

kong/basic_auth_service_test.go

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,3 +398,194 @@ func TestBasicAuthListMethods(T *testing.T) {
398398
require.NoError(client.Consumers.Delete(defaultCtx, consumer1.ID))
399399
require.NoError(client.Consumers.Delete(defaultCtx, consumer2.ID))
400400
}
401+
402+
func TestBasicAuthCreateWithOptions(T *testing.T) {
403+
RunWhenDBMode(T, "postgres")
404+
405+
assert := assert.New(T)
406+
require := require.New(T)
407+
408+
client, err := NewTestClient(nil, nil)
409+
require.NoError(err)
410+
assert.NotNil(client)
411+
412+
// Test with nil options
413+
basicAuth, err := client.BasicAuths.CreateWithOptions(defaultCtx,
414+
String("foo"), nil)
415+
require.Error(err)
416+
assert.Nil(basicAuth)
417+
assert.Contains(err.Error(), "basic auth options and credential are required")
418+
419+
// consumer for the basic-auth:
420+
consumer := &Consumer{
421+
Username: String("foo"),
422+
}
423+
424+
consumer, err = client.Consumers.Create(defaultCtx, consumer)
425+
require.NoError(err)
426+
require.NotNil(consumer)
427+
428+
// Test with empty consumer ID
429+
opts := &BasicAuthOptions{
430+
BasicAuth: BasicAuth{
431+
Username: String("test-username"),
432+
Password: String("test-password"),
433+
},
434+
}
435+
basicAuth, err = client.BasicAuths.CreateWithOptions(defaultCtx,
436+
String(""), opts)
437+
require.Error(err)
438+
assert.Nil(basicAuth)
439+
440+
// Test with non-existent consumer
441+
basicAuth, err = client.BasicAuths.CreateWithOptions(defaultCtx,
442+
String("does-not-exist"), opts)
443+
require.Error(err)
444+
assert.Nil(basicAuth)
445+
446+
// Test with missing username
447+
optsNoUsername := &BasicAuthOptions{
448+
BasicAuth: BasicAuth{
449+
Password: String("test-password"),
450+
},
451+
}
452+
basicAuth, err = client.BasicAuths.CreateWithOptions(defaultCtx,
453+
consumer.ID, optsNoUsername)
454+
require.Error(err)
455+
assert.Nil(basicAuth)
456+
457+
// Test successful creation with SkipHash = false
458+
uuid1 := uuid.NewString()
459+
skipHashFalse := false
460+
opts = &BasicAuthOptions{
461+
BasicAuth: BasicAuth{
462+
ID: String(uuid1),
463+
Username: String("test-username"),
464+
Password: String("test-password"),
465+
},
466+
SkipHash: &skipHashFalse,
467+
}
468+
basicAuth, err = client.BasicAuths.CreateWithOptions(defaultCtx,
469+
consumer.ID, opts)
470+
require.NoError(err)
471+
assert.NotNil(basicAuth)
472+
assert.Equal(uuid1, *basicAuth.ID)
473+
assert.Equal("test-username", *basicAuth.Username)
474+
// password should be hashed when SkipHash is false
475+
assert.NotEqual("test-password", *basicAuth.Password)
476+
477+
// Clean up
478+
err = client.BasicAuths.Delete(defaultCtx, consumer.ID, basicAuth.Username)
479+
require.NoError(err)
480+
481+
// Test with SkipHash = true can work only with Konnect for now
482+
// TODO: enable this test for Konnect when we have a way to run tests against Konnect
483+
484+
// Test successful creation with nil SkipHash (should default to false)
485+
uuid3 := uuid.NewString()
486+
opts = &BasicAuthOptions{
487+
BasicAuth: BasicAuth{
488+
ID: String(uuid3),
489+
Username: String("test-username-3"),
490+
Password: String("test-password-3"),
491+
},
492+
SkipHash: nil,
493+
}
494+
basicAuthNilSkip, err := client.BasicAuths.CreateWithOptions(defaultCtx,
495+
consumer.ID, opts)
496+
require.NoError(err)
497+
assert.NotNil(basicAuthNilSkip)
498+
assert.Equal(uuid3, *basicAuthNilSkip.ID)
499+
assert.Equal("test-username-3", *basicAuthNilSkip.Username)
500+
// password should be hashed when SkipHash is nil (defaults to false)
501+
assert.NotEqual("test-password-3", *basicAuthNilSkip.Password)
502+
503+
require.NoError(client.Consumers.Delete(defaultCtx, consumer.ID))
504+
}
505+
506+
func TestBasicAuthUpdateWithOptions(T *testing.T) {
507+
RunWhenDBMode(T, "postgres")
508+
509+
assert := assert.New(T)
510+
require := require.New(T)
511+
512+
client, err := NewTestClient(nil, nil)
513+
require.NoError(err)
514+
assert.NotNil(client)
515+
516+
// consumer for the basic-auth:
517+
consumer := &Consumer{
518+
Username: String("foo"),
519+
}
520+
521+
consumer, err = client.Consumers.Create(defaultCtx, consumer)
522+
require.NoError(err)
523+
require.NotNil(consumer)
524+
525+
// Create initial basic auth credential
526+
uuidStr := uuid.NewString()
527+
basicAuth := &BasicAuth{
528+
ID: String(uuidStr),
529+
Username: String("initial-username"),
530+
Password: String("initial-password"),
531+
}
532+
533+
createdBasicAuth, err := client.BasicAuths.Create(defaultCtx,
534+
consumer.ID, basicAuth)
535+
require.NoError(err)
536+
assert.NotNil(createdBasicAuth)
537+
538+
// Test with nil options
539+
updatedBasicAuth, err := client.BasicAuths.UpdateWithOptions(defaultCtx,
540+
consumer.ID, nil)
541+
require.Error(err)
542+
assert.Nil(updatedBasicAuth)
543+
assert.Contains(err.Error(), "basic auth options and credential are required")
544+
545+
// Test successful update with SkipHash = false
546+
skipHashFalse := false
547+
opts := &BasicAuthOptions{
548+
BasicAuth: BasicAuth{
549+
ID: createdBasicAuth.ID,
550+
Username: String("updated-username"),
551+
Password: String("updated-password"),
552+
},
553+
SkipHash: &skipHashFalse,
554+
}
555+
updatedBasicAuth, err = client.BasicAuths.UpdateWithOptions(defaultCtx,
556+
consumer.ID, opts)
557+
require.NoError(err)
558+
assert.NotNil(updatedBasicAuth)
559+
assert.Equal("updated-username", *updatedBasicAuth.Username)
560+
// password should be hashed when SkipHash is false
561+
assert.NotEqual("updated-password", *updatedBasicAuth.Password)
562+
563+
// Test successful update with SkipHash = true
564+
// Test with SkipHash = true can work only with Konnect for now
565+
// TODO: enable this test for Konnect when we have a way to run tests against Konnect
566+
567+
// Test successful update with nil SkipHash (should default to false)
568+
opts = &BasicAuthOptions{
569+
BasicAuth: BasicAuth{
570+
ID: createdBasicAuth.ID,
571+
Username: String("updated-username-3"),
572+
Password: String("updated-password-3"),
573+
},
574+
SkipHash: nil,
575+
}
576+
updatedBasicAuth, err = client.BasicAuths.UpdateWithOptions(defaultCtx,
577+
consumer.ID, opts)
578+
require.NoError(err)
579+
assert.NotNil(updatedBasicAuth)
580+
assert.Equal("updated-username-3", *updatedBasicAuth.Username)
581+
// password should be hashed when SkipHash is nil (defaults to false)
582+
assert.NotEqual("updated-password-3", *updatedBasicAuth.Password)
583+
584+
// Verify the credential was actually updated
585+
retrievedBasicAuth, err := client.BasicAuths.Get(defaultCtx,
586+
consumer.ID, updatedBasicAuth.Username)
587+
require.NoError(err)
588+
assert.Equal("updated-username-3", *retrievedBasicAuth.Username)
589+
590+
require.NoError(client.Consumers.Delete(defaultCtx, consumer.ID))
591+
}

0 commit comments

Comments
 (0)