Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unable to implement mock Verifier for testing introspection #299

Closed
0xd3e opened this issue Feb 13, 2024 · 3 comments
Closed

Unable to implement mock Verifier for testing introspection #299

0xd3e opened this issue Feb 13, 2024 · 3 comments
Assignees
Labels
auth waiting waiting on changes or reply after review or comments

Comments

@0xd3e
Copy link

0xd3e commented Feb 13, 2024

Version: v3.0.0-next.2

I'm trying to implement a Verifier that doesn't actually call the Zitadel instance for testing purposes. As it looks to me now, this is either hard to do or even impossible due to the use of generics. I'm also kinda new to the use of generics so maybe I do lack some knowledge here.

My current approach is to create two different Authorizers, but when I pass that as function argument to my middleware / interceptor, I obviously need to declare the type of the function argument, which is either *authorization.Authorizer[*oauth.IntrospectionContext] or *authorization.Authorizer[*authz.ZitadelMockIntrospectionContext], which is my own implementation of authorization.Ctx

Can you tell me if it is possible to do what I'm trying to do, and if so give me a hint what I'm doing wrong?

// main.go
var mockAuthorizer *authorization.Authorizer[*authz.ZitadelMockIntrospectionContext]
{
	mockAuthorizer, err = authorization.New[*authz.ZitadelMockIntrospectionContext](
		ctx,
		zitadel.New("mock.zitatdel.local"), // Should not exist.
		func(
			ctx context.Context,
			zitadel *zitadel.Zitadel,
		) (authorization.Verifier[*authz.ZitadelMockIntrospectionContext], error) {
			return authz.ZitadelMockVerifier[*authz.ZitadelMockIntrospectionContext]{}, nil
		},
	)
}

var oauthAuthorizer *authorization.Authorizer[*oauth.IntrospectionContext]
{
	oauthAuthorizer, err = authorization.New[*oauth.IntrospectionContext](
		ctx,
		zitadel.New(conf.Zitadel.Domain),
		oauth.DefaultAuthorization(conf.Zitadel.KeyPath),
		authorization.WithLogger[*oauth.IntrospectionContext](
			zitadellog.New(
				zitadellog.NewTextHandler(
					io.Discard, // Don't log anything from Zitadel
					&zitadellog.HandlerOptions{},
				),
			),
		),
	)
}

authorizer := oauthAuthorizer
if conf.DevelopmentMode {
	authorizer := mockAuthorizer
}

// Create HTTP server
var mux *http.ServeMux = http.NewServeMux()

// Initialize project service
var projectSvc *project.Service = project.New(...)

var projectPath string
var projectHandler http.Handler
{
	projectPath, projectHandler = projecttransport.New(
		...
		authorizer,
	)
}
// projecttransport/server.go

func New(
	...
	authorizer *authorization.Authorizer[...], // <--- What to use?
) (string, http.Handler) {
	var srv *Server = &Server{
		...
	}

	var interceptors connect.Option = connect.WithInterceptors(
		transport.NewAuthzInterceptor(authorizer),
	}

	var path string
	var handler http.Handler
	{
		...
	}

	return path, handler
}

Tasks

Preview Give feedback
No tasks being tracked yet.
@hifabienne hifabienne moved this to 🧐 Investigating in Product Management Feb 14, 2024
@hifabienne
Copy link
Member

thanks, we will have a look at it

@livio-a livio-a self-assigned this Mar 20, 2024
@livio-a
Copy link
Member

livio-a commented Apr 10, 2024

Hey @0x1DA117

I finally found some time to look into this and did a small test.

You can exchange the authorize checks by providing your own VerifierInitializer[T] in the authorization.New:

authZ, err := authorization.New(ctx, zitadel.New(*domain), mockAuth())

where the function and used mocks look like this:

func mockAuth() authorization.VerifierInitializer[*mockCtx] {
	return func(ctx context.Context, zitadel *zitadel.Zitadel) (authorization.Verifier[*mockCtx], error) {
		return &mockVerifier{}, nil
	}
}

type mockCtx struct {
	userID   string
	token    string
	Username string // add username so the example/api/http works with the current state
}

func (m *mockCtx) IsAuthorized() bool {
	return true
}

func (m *mockCtx) UserID() string {
	return m.userID
}

func (m *mockCtx) IsGrantedRole(role string) bool {
	return true
}

func (m *mockCtx) IsGrantedRoleInOrganization(role, organizationID string) bool {
	return true
}

func (m *mockCtx) SetToken(token string) {
	m.token = token
}

func (m *mockCtx) GetToken() string {
	return m.token
}

type mockVerifier struct {}

func (m *mockVerifier) CheckAuthorization(ctx context.Context, authorizationToken string) (*mockCtx, error) {
	return &mockCtx{userID: "id", Username: "test"}, nil
}

@livio-a livio-a added the waiting waiting on changes or reply after review or comments label Apr 10, 2024
@0xd3e
Copy link
Author

0xd3e commented Apr 10, 2024

Thanks @livio-a, currently I do not have time to implement it, but I'll link this issue in my own issue. I'll close this issue for now as it seems to be solved.

When I have time to work on this again and a question arises, I'll reopen it.

@0xd3e 0xd3e closed this as completed Apr 10, 2024
@github-project-automation github-project-automation bot moved this from 🧐 Investigating to ✅ Done in Product Management Apr 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
auth waiting waiting on changes or reply after review or comments
Projects
Archived in project
Development

No branches or pull requests

3 participants