Skip to content

Commit a4052f4

Browse files
feat: v0.6.0 (#366)
* feat(idtoken-auth): add auth validator using google idtoken (#349) * docs: add idtoken validation on sample config.yaml * feat: add auth config to replace idtoken validator config, and refactor default auth header key * refactor: move auth interceptor to pkg/auth * chore: delete idtoken validator mock * feat: keep config.AuthenticatedUserHeaderKey for backward-compatibility * chore: re-order import * refactor: change sample config.yaml for auth config * chore: make auth user header key sample and default config consistent * chore: re-order import on oidc test * fix: change oidc initialism * fix: oidc validator mocks renaming * fix: avoid using params with pointer, as it will lead to panic if params is nil * fix: detect old auth user header key using empty string instead of default tag * refactor: move OIDCAuth to pkg/auth * refactor: move oidc validator mocks into pkg/auth/mocks * chore: add deprecation notes on AuthenticatedUserHeaderKey * refactor: make default auth email context key back to unexported, use different context key for oidc email * refactor: do not use default header key on oidc auth, use its own header (it's only used for logrus) * refactor: move logrus context custom fields to new interceptor and retrieve its value from context * refactor: auth email context key mapping for default and oidc used for grpc server * test: change ways of getting user email from request header to context * refactor: use auth.OIDCAuth instead of OIDCValidatorParams * feat(gcs): support import grants for gcs provider (#360) * feat(gcs): support import grants for gcs provider * refactor(gcs): use projectID value from struct field and remove from GetBuckets' param * refactor(gcs): refactor repeated logic to retrieve gcs client * refactor(gcs): improve code readability * fix(gcs): exclude deleted principals (#364) * refactor(gcs): use account type whitelisting * fix(gcs): exclude deleted principals * fix(gcs): fix timeout by running getPolicy in parallel (#365) --------- Co-authored-by: Pulung Ragil <[email protected]>
1 parent 575dfc7 commit a4052f4

File tree

18 files changed

+761
-186
lines changed

18 files changed

+761
-186
lines changed

api/handler/v1beta1/appeal_test.go

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -128,11 +128,7 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
128128
ResourceUrns: []string{"test-resource-urn"},
129129
OrderBy: []string{"test-order"},
130130
}
131-
ctx := context.Background()
132-
md := metadata.New(map[string]string{
133-
s.authenticatedUserHeaderKey: expectedUser,
134-
})
135-
ctx = metadata.NewIncomingContext(ctx, md)
131+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, expectedUser)
136132
res, err := s.grpcServer.ListUserAppeals(ctx, req)
137133

138134
s.NoError(err)
@@ -162,11 +158,7 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
162158
Return(nil, expectedError).Once()
163159

164160
req := &guardianv1beta1.ListUserAppealsRequest{}
165-
ctx := context.Background()
166-
md := metadata.New(map[string]string{
167-
s.authenticatedUserHeaderKey: "test-user",
168-
})
169-
ctx = metadata.NewIncomingContext(ctx, md)
161+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "test-user")
170162
res, err := s.grpcServer.ListUserAppeals(ctx, req)
171163

172164
s.Equal(codes.Internal, status.Code(err))
@@ -188,11 +180,7 @@ func (s *GrpcHandlersSuite) TestListUserAppeals() {
188180
Return(invalidAppeals, nil).Once()
189181

190182
req := &guardianv1beta1.ListUserAppealsRequest{}
191-
ctx := context.Background()
192-
md := metadata.New(map[string]string{
193-
s.authenticatedUserHeaderKey: "test-user",
194-
})
195-
ctx = metadata.NewIncomingContext(ctx, md)
183+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "test-user")
196184
res, err := s.grpcServer.ListUserAppeals(ctx, req)
197185

198186
s.Equal(codes.Internal, status.Code(err))
@@ -484,11 +472,7 @@ func (s *GrpcHandlersSuite) TestCreateAppeal() {
484472
},
485473
Description: "The answer is 42",
486474
}
487-
ctx := context.Background()
488-
md := metadata.New(map[string]string{
489-
s.authenticatedUserHeaderKey: expectedUser,
490-
})
491-
ctx = metadata.NewIncomingContext(ctx, md)
475+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, expectedUser)
492476
res, err := s.grpcServer.CreateAppeal(ctx, req)
493477

494478
s.NoError(err)
@@ -520,11 +504,7 @@ func (s *GrpcHandlersSuite) TestCreateAppeal() {
520504
s.appealService.EXPECT().Create(mock.AnythingOfType("*context.valueCtx"), mock.Anything).Return(appeal.ErrAppealDuplicate).Once()
521505

522506
req := &guardianv1beta1.CreateAppealRequest{}
523-
ctx := context.Background()
524-
md := metadata.New(map[string]string{
525-
s.authenticatedUserHeaderKey: "[email protected]",
526-
})
527-
ctx = metadata.NewIncomingContext(ctx, md)
507+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "[email protected]")
528508
res, err := s.grpcServer.CreateAppeal(ctx, req)
529509

530510
s.Equal(codes.AlreadyExists, status.Code(err))
@@ -539,11 +519,7 @@ func (s *GrpcHandlersSuite) TestCreateAppeal() {
539519
s.appealService.EXPECT().Create(mock.AnythingOfType("*context.valueCtx"), mock.Anything).Return(expectedError).Once()
540520

541521
req := &guardianv1beta1.CreateAppealRequest{}
542-
ctx := context.Background()
543-
md := metadata.New(map[string]string{
544-
s.authenticatedUserHeaderKey: "[email protected]",
545-
})
546-
ctx = metadata.NewIncomingContext(ctx, md)
522+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "[email protected]")
547523
res, err := s.grpcServer.CreateAppeal(ctx, req)
548524

549525
s.Equal(codes.Internal, status.Code(err))
@@ -567,11 +543,7 @@ func (s *GrpcHandlersSuite) TestCreateAppeal() {
567543
Return(nil).Once()
568544

569545
req := &guardianv1beta1.CreateAppealRequest{Resources: make([]*guardianv1beta1.CreateAppealRequest_Resource, 1)}
570-
ctx := context.Background()
571-
md := metadata.New(map[string]string{
572-
s.authenticatedUserHeaderKey: "[email protected]",
573-
})
574-
ctx = metadata.NewIncomingContext(ctx, md)
546+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "[email protected]")
575547
res, err := s.grpcServer.CreateAppeal(ctx, req)
576548

577549
s.Equal(codes.Internal, status.Code(err))

api/handler/v1beta1/approval_test.go

Lines changed: 6 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,7 @@ func (s *GrpcHandlersSuite) TestListUserApprovals() {
116116
Statuses: []string{"active", "pending"},
117117
OrderBy: []string{"test-order"},
118118
}
119-
ctx := context.Background()
120-
md := metadata.New(map[string]string{
121-
s.authenticatedUserHeaderKey: expectedUser,
122-
})
123-
ctx = metadata.NewIncomingContext(ctx, md)
119+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, expectedUser)
124120
res, err := s.grpcServer.ListUserApprovals(ctx, req)
125121

126122
s.NoError(err)
@@ -150,11 +146,7 @@ func (s *GrpcHandlersSuite) TestListUserApprovals() {
150146
Return(nil, expectedError).Once()
151147

152148
req := &guardianv1beta1.ListUserApprovalsRequest{}
153-
ctx := context.Background()
154-
md := metadata.New(map[string]string{
155-
s.authenticatedUserHeaderKey: "test-user",
156-
})
157-
ctx = metadata.NewIncomingContext(ctx, md)
149+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "test-user")
158150
res, err := s.grpcServer.ListUserApprovals(ctx, req)
159151

160152
s.Equal(codes.Internal, status.Code(err))
@@ -178,11 +170,7 @@ func (s *GrpcHandlersSuite) TestListUserApprovals() {
178170
Return(invalidApprovals, nil).Once()
179171

180172
req := &guardianv1beta1.ListUserApprovalsRequest{}
181-
ctx := context.Background()
182-
md := metadata.New(map[string]string{
183-
s.authenticatedUserHeaderKey: "test-user",
184-
})
185-
ctx = metadata.NewIncomingContext(ctx, md)
173+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "test-user")
186174
res, err := s.grpcServer.ListUserApprovals(ctx, req)
187175

188176
s.Equal(codes.Internal, status.Code(err))
@@ -443,11 +431,7 @@ func (s *GrpcHandlersSuite) TestUpdateApproval() {
443431
Reason: expectedReason,
444432
},
445433
}
446-
ctx := context.Background()
447-
md := metadata.New(map[string]string{
448-
s.authenticatedUserHeaderKey: expectedUser,
449-
})
450-
ctx = metadata.NewIncomingContext(ctx, md)
434+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, expectedUser)
451435
res, err := s.grpcServer.UpdateApproval(ctx, req)
452436

453437
s.NoError(err)
@@ -553,11 +537,7 @@ func (s *GrpcHandlersSuite) TestUpdateApproval() {
553537
Return(nil, tc.expectedError).Once()
554538

555539
req := &guardianv1beta1.UpdateApprovalRequest{}
556-
ctx := context.Background()
557-
md := metadata.New(map[string]string{
558-
s.authenticatedUserHeaderKey: expectedUser,
559-
})
560-
ctx = metadata.NewIncomingContext(ctx, md)
540+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, expectedUser)
561541
res, err := s.grpcServer.UpdateApproval(ctx, req)
562542

563543
s.Equal(tc.expectedStatusCode, status.Code(err))
@@ -579,11 +559,7 @@ func (s *GrpcHandlersSuite) TestUpdateApproval() {
579559
Return(invalidAppeal, nil).Once()
580560

581561
req := &guardianv1beta1.UpdateApprovalRequest{}
582-
ctx := context.Background()
583-
md := metadata.New(map[string]string{
584-
s.authenticatedUserHeaderKey: "[email protected]",
585-
})
586-
ctx = metadata.NewIncomingContext(ctx, md)
562+
ctx := context.WithValue(context.Background(), authEmailTestContextKey{}, "[email protected]")
587563
res, err := s.grpcServer.UpdateApproval(ctx, req)
588564

589565
s.Equal(codes.Internal, status.Code(err))

api/handler/v1beta1/grpc.go

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ package v1beta1
22

33
import (
44
"context"
5-
"errors"
5+
"strings"
66

77
"github.com/odpf/guardian/core/appeal"
88
"github.com/odpf/guardian/core/grant"
99

1010
guardianv1beta1 "github.com/odpf/guardian/api/proto/odpf/guardian/v1beta1"
1111
"github.com/odpf/guardian/domain"
12-
"google.golang.org/grpc/metadata"
12+
"google.golang.org/grpc/codes"
13+
"google.golang.org/grpc/status"
1314
)
1415

1516
type ProtoAdapter interface {
@@ -116,7 +117,7 @@ type GRPCServer struct {
116117
grantService grantService
117118
adapter ProtoAdapter
118119

119-
authenticatedUserHeaderKey string
120+
authenticatedUserContextKey interface{}
120121

121122
guardianv1beta1.UnimplementedGuardianServiceServer
122123
}
@@ -130,32 +131,30 @@ func NewGRPCServer(
130131
approvalService approvalService,
131132
grantService grantService,
132133
adapter ProtoAdapter,
133-
authenticatedUserHeaderKey string,
134+
authenticatedUserContextKey interface{},
134135
) *GRPCServer {
135136
return &GRPCServer{
136-
resourceService: resourceService,
137-
activityService: activityService,
138-
providerService: providerService,
139-
policyService: policyService,
140-
appealService: appealService,
141-
approvalService: approvalService,
142-
grantService: grantService,
143-
adapter: adapter,
144-
authenticatedUserHeaderKey: authenticatedUserHeaderKey,
137+
resourceService: resourceService,
138+
activityService: activityService,
139+
providerService: providerService,
140+
policyService: policyService,
141+
appealService: appealService,
142+
approvalService: approvalService,
143+
grantService: grantService,
144+
adapter: adapter,
145+
authenticatedUserContextKey: authenticatedUserContextKey,
145146
}
146147
}
147148

148149
func (s *GRPCServer) getUser(ctx context.Context) (string, error) {
149-
md, ok := metadata.FromIncomingContext(ctx)
150+
authenticatedEmail, ok := ctx.Value(s.authenticatedUserContextKey).(string)
150151
if !ok {
151-
return "", errors.New("unable to retrieve metadata from context")
152+
return "", status.Error(codes.Unauthenticated, "unable to get authenticated user from context")
152153
}
153154

154-
users := md.Get(s.authenticatedUserHeaderKey)
155-
if len(users) == 0 {
156-
return "", errors.New("user email not found")
155+
if strings.TrimSpace(authenticatedEmail) == "" {
156+
return "", status.Error(codes.Unauthenticated, "unable to get authenticated user from context")
157157
}
158158

159-
currentUser := users[0]
160-
return currentUser, nil
159+
return authenticatedEmail, nil
161160
}

api/handler/v1beta1/grpc_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"github.com/stretchr/testify/suite"
99
)
1010

11+
type authEmailTestContextKey struct{}
12+
1113
type GrpcHandlersSuite struct {
1214
suite.Suite
1315

@@ -19,8 +21,6 @@ type GrpcHandlersSuite struct {
1921
approvalService *mocks.ApprovalService
2022
grantService *mocks.GrantService
2123
grpcServer *v1beta1.GRPCServer
22-
23-
authenticatedUserHeaderKey string
2424
}
2525

2626
func TestGrpcHandler(t *testing.T) {
@@ -35,7 +35,6 @@ func (s *GrpcHandlersSuite) setup() {
3535
s.appealService = new(mocks.AppealService)
3636
s.approvalService = new(mocks.ApprovalService)
3737
s.grantService = new(mocks.GrantService)
38-
s.authenticatedUserHeaderKey = "test-header-key"
3938
s.grpcServer = v1beta1.NewGRPCServer(
4039
s.resourceService,
4140
s.activityService,
@@ -45,6 +44,6 @@ func (s *GrpcHandlersSuite) setup() {
4544
s.approvalService,
4645
s.grantService,
4746
v1beta1.NewAdapter(),
48-
s.authenticatedUserHeaderKey,
47+
authEmailTestContextKey{},
4948
)
5049
}

internal/server/auth.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,29 @@ import (
1111

1212
type authenticatedUserEmailContextKey struct{}
1313

14+
var logrusActorKey = "actor"
15+
1416
func withAuthenticatedUserEmail(headerKey string) grpc.UnaryServerInterceptor {
1517
return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
1618
if md, ok := metadata.FromIncomingContext(ctx); ok {
1719
if v := md.Get(headerKey); len(v) > 0 {
1820
userEmail := v[0]
1921
ctx = context.WithValue(ctx, authenticatedUserEmailContextKey{}, userEmail)
20-
21-
ctx_logrus.AddFields(ctx, logrus.Fields{
22-
headerKey: userEmail,
23-
})
2422
}
2523
}
2624

2725
return handler(ctx, req)
2826
}
2927
}
28+
29+
func withLogrusContext() grpc.UnaryServerInterceptor {
30+
return func(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
31+
if userEmail, ok := ctx.Value(authenticatedUserEmailContextKey{}).(string); ok {
32+
ctx_logrus.AddFields(ctx, logrus.Fields{
33+
logrusActorKey: userEmail,
34+
})
35+
}
36+
37+
return handler(ctx, req)
38+
}
39+
}

internal/server/config.go

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66

77
"github.com/odpf/guardian/internal/store"
8+
"github.com/odpf/guardian/pkg/auth"
89
"github.com/odpf/guardian/pkg/tracing"
910
"github.com/odpf/guardian/plugins/notifiers"
1011
"github.com/odpf/salt/config"
@@ -33,16 +34,28 @@ type Jobs struct {
3334
ExpiringAccessNotification JobConfig `mapstructure:"expiring_access_notification"`
3435
}
3536

37+
type DefaultAuth struct {
38+
HeaderKey string `mapstructure:"header_key" default:"X-Auth-Email"`
39+
}
40+
41+
type Auth struct {
42+
Provider string `mapstructure:"provider" default:"default"`
43+
Default DefaultAuth `mapstructure:"default"`
44+
OIDC auth.OIDCAuth `mapstructure:"oidc"`
45+
}
46+
3647
type Config struct {
37-
Port int `mapstructure:"port" default:"8080"`
38-
EncryptionSecretKeyKey string `mapstructure:"encryption_secret_key"`
39-
Notifier notifiers.Config `mapstructure:"notifier"`
40-
LogLevel string `mapstructure:"log_level" default:"info"`
41-
DB store.Config `mapstructure:"db"`
42-
AuthenticatedUserHeaderKey string `mapstructure:"authenticated_user_header_key"`
43-
AuditLogTraceIDHeaderKey string `mapstructure:"audit_log_trace_id_header_key" default:"X-Trace-Id"`
44-
Jobs Jobs `mapstructure:"jobs"`
45-
Telemetry tracing.Config `mapstructure:"telemetry"`
48+
Port int `mapstructure:"port" default:"8080"`
49+
EncryptionSecretKeyKey string `mapstructure:"encryption_secret_key"`
50+
Notifier notifiers.Config `mapstructure:"notifier"`
51+
LogLevel string `mapstructure:"log_level" default:"info"`
52+
DB store.Config `mapstructure:"db"`
53+
// Deprecated: use Auth.Default.HeaderKey instead note on the AuthenticatedUserHeaderKey
54+
AuthenticatedUserHeaderKey string `mapstructure:"authenticated_user_header_key"`
55+
AuditLogTraceIDHeaderKey string `mapstructure:"audit_log_trace_id_header_key" default:"X-Trace-Id"`
56+
Jobs Jobs `mapstructure:"jobs"`
57+
Telemetry tracing.Config `mapstructure:"telemetry"`
58+
Auth Auth `mapstructure:"auth"`
4659
}
4760

4861
func LoadConfig(configFile string) (Config, error) {
@@ -56,5 +69,11 @@ func LoadConfig(configFile string) (Config, error) {
5669
}
5770
return Config{}, err
5871
}
72+
73+
// keep for backward-compatibility
74+
if cfg.AuthenticatedUserHeaderKey != "" {
75+
cfg.Auth.Default.HeaderKey = cfg.AuthenticatedUserHeaderKey
76+
}
77+
5978
return cfg, nil
6079
}

internal/server/config.yaml

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
PORT: 3000
1313
ENCRYPTION_SECRET_KEY:
14-
AUTHENTICATED_USER_HEADER_KEY: X-User-Email
14+
AUTHENTICATED_USER_HEADER_KEY: X-Auth-Email
1515
LOG:
1616
LEVEL: info
1717
DB:
@@ -42,4 +42,11 @@ TELEMETRY:
4242
OTLP:
4343
HEADERS:
4444
api-key: <YOUR-LICENSE-KEY>
45-
ENDPOINT: "otlp.nr-data.net:4317"
45+
ENDPOINT: "otlp.nr-data.net:4317"
46+
AUTH:
47+
PROVIDER: default # can be "default" or "oidc"
48+
DEFAULT:
49+
HEADER_KEY: X-Auth-Email # AUTHENTICATED_USER_HEADER_KEY takes priority for backward-compatibility
50+
OIDC:
51+
AUDIENCE: "some-kind-of-audience.com"
52+
ELIGIBLE_EMAIL_DOMAINS: "emaildomain1.com,emaildomain2.com"

0 commit comments

Comments
 (0)