Skip to content

Commit

Permalink
wip: oas authenticator
Browse files Browse the repository at this point in the history
  • Loading branch information
katallaxie authored Jul 24, 2024
1 parent 894cd93 commit 363faaa
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 3 deletions.
5 changes: 5 additions & 0 deletions oas/oas.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ func Authenticate(opts ...AuthenticatorOpt) openapi3filter.AuthenticationFunc {
return fiber.ErrForbidden
}

err := auth(ctx, input)
if err != nil {
return err
}

return auth(ctx, input)
}
}
8 changes: 5 additions & 3 deletions openfga/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (

"github.com/gofiber/fiber/v2"
"github.com/openfga/go-sdk/client"
"github.com/zeiss/fiber-authz/oas"
"github.com/zeiss/fiber-authz/oas/oidc"
)

// DefaultSeparator is the default separator for entities.
Expand Down Expand Up @@ -93,9 +93,11 @@ func Join(sep string, entities ...string) Stringer {
}

// OidcSubject returns the OIDC subject.
func OidcSubject(claims *oas.AuthClaims) Stringer {
func OidcSubject(ctx context.Context) Stringer {
claim, _ := oidc.GetJWTFromContext(ctx)

return func() string {
return claims.Subject
return claim.Subject
}
}

Expand Down
220 changes: 220 additions & 0 deletions openfga/oas.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
package openfga

import (
"context"

"github.com/getkin/kin-openapi/openapi3filter"
"github.com/gofiber/fiber/v2"
)

// OasFGAAuthzOptionComponent ...
type OasFGAAuthzOptionComponent struct {
In string `json:"in"`
Name string `json:"name"`
Type string `json:"type"`
}

// OasFGAAuthzOption ...
type OasFGAAuthzBuilderOption struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Seperator string `json:"seperator"`

Check failure on line 21 in openfga/oas.go

View workflow job for this annotation

GitHub Actions / lint

`Seperator` is a misspelling of `Separator` (misspell)
Components []OasFGAAuthzOptionComponent `json:"components"`
AuthType string `json:"auth_type"`
}

// OasFGAAuthzBuilderOptions ...
type OasFGAAuthzBuilderOptions struct {
User OasFGAAuthzBuilderOption `json:"user"`
Object OasFGAAuthzBuilderOption `json:"object"`
Relation OasFGAAuthzBuilderOption `json:"relation"`
}

// OasBuilder ...
type OasBuilder interface {
// BuildWithContext builds a user, relation, and object with a context.
BuildWithContext(ctx context.Context, input *openapi3filter.AuthenticationInput) (User, Relation, Object, error)
}

// OasFGAAuthzBuilder ...
type OasFGAAuthzBuilder struct {
Options OasFGAAuthzBuilderOptions `json:"options"`
}

// BuildWithContext ...
func (f *OasFGAAuthzBuilder) BuildWithContext(ctx context.Context, input *openapi3filter.AuthenticationInput) (User, Relation, Object, error) {
return f.User(ctx, input), f.Relation(ctx, input), f.Object(ctx, input), nil
}

// User ...
func (f *OasFGAAuthzBuilder) User(ctx context.Context, input *openapi3filter.AuthenticationInput) User {
return NewUser(Namespace(f.Options.User.Namespace), OidcSubject(ctx))
}

// Object ...
func (f *OasFGAAuthzBuilder) Object(ctx context.Context, input *openapi3filter.AuthenticationInput) Object {
ss := []string{}

for _, c := range f.Options.Object.Components {
switch c.In {
case "path":
ss = append(ss, PathParams(input.RequestValidationInput.PathParams, c.Name))
default:
ss = append(ss, "")
}
}

return NewObject(Namespace(f.Options.Object.Namespace), Join(f.Options.Object.Seperator, ss...))

Check failure on line 67 in openfga/oas.go

View workflow job for this annotation

GitHub Actions / lint

`Seperator` is a misspelling of `Separator` (misspell)
}

// OasAuthenticateOpts ...
type OasAuthenticateOpts struct {
Checker Checker
Builder OasBuilder
}

// OasAuthenticateOpts ...
func (c *OasAuthenticateOpts) Configure(opts ...OasAuthenticateOpt) {
for _, opt := range opts {
opt(c)
}
}

// OasAuthenticateOpt ...
type OasAuthenticateOpt func(*OasAuthenticateOpts)

// OasDefaultAuthenticateOpts ...
func OasDefaultAuthenticateOpts() OasAuthenticateOpts {
return OasAuthenticateOpts{
Builder: &OasFGAAuthzBuilder{},
}
}

// WithChecker sets the checker for the authenticator.
func WithChecker(checker Checker) OasAuthenticateOpt {
return func(o *OasAuthenticateOpts) {
o.Checker = checker
}
}

// OasAuthenticate ...
func OasAuthenticate(opts ...OasAuthenticateOpt) openapi3filter.AuthenticationFunc {
options := OasDefaultAuthenticateOpts()
options.Configure(opts...)

return func(ctx context.Context, input *openapi3filter.AuthenticationInput) error {
user, relation, object, err := options.Builder.BuildWithContext(ctx, input)
if err != nil {
return err
}

allowed, err := options.Checker.Allowed(ctx, user, relation, object)
if err != nil {
return fiber.ErrUnauthorized
}

if !allowed {
return fiber.ErrForbidden
}

return nil
}
}

// PathParam ...
func PathParams(params map[string]string, name string, v ...string) string {
s := ""

if len(v) > 0 {
s = v[0]
}

p, ok := params[name]
if !ok {
return s
}

return p
}

// Relation ...
func (f *OasFGAAuthzBuilder) Relation(ctx context.Context, input *openapi3filter.AuthenticationInput) Relation {
return NewRelation(String(f.Options.User.Name))
}

// ResolverFunc is a function that resolves a user, relation, and object.
type ResolverFunc func(c *fiber.Ctx) (User, Relation, Object, error)

// AuthzController is a controller that handles authorization.
type AuthzController interface {
// Authorize authorizes a user, relation, and object.
Authorize(c *fiber.Ctx) (User, Relation, Object, error)
}

// OperationAuthorizeFunc ...
type OperationAuthorizeFunc func(ctx context.Context, input *openapi3filter.AuthenticationInput) error

// OperationAuthorizers ...
type OperationAuthorizers map[string]OperationAuthorizeFunc

// Add is a function that adds an operation authorizer.
func (o OperationAuthorizers) Add(op string, f OperationAuthorizeFunc) {
o[op] = f
}

// AuthorizerOpts ...
type AuthorizerOpts struct {
// Authorizers is a map of operation authorizers.
Authorizers OperationAuthorizers
// DefaultAuthorizer is the default authorizer.
DefaultAuthorizer OperationAuthorizeFunc
}

// Configure sets the configuration for the authorizer.
func (c *AuthorizerOpts) Configure(opts ...AuthorizerOpt) {
for _, opt := range opts {
opt(c)
}
}

// AuthorizerOpt ...
type AuthorizerOpt func(*AuthorizerOpts)

// WithAuthorizers sets the authorizers for the authorizer.
func WithAuthorizers(authorizers OperationAuthorizers) AuthorizerOpt {
return func(o *AuthorizerOpts) {
o.Authorizers = authorizers
}
}

// DefaultAuthorizer ...
var DefaultAuthorizer = func(ctx context.Context, input *openapi3filter.AuthenticationInput) error {
return fiber.ErrForbidden
}

// DefaultAuthorizerOpts returns the default authorizer options.
func DefaultAuthorizerOpts() AuthorizerOpts {
return AuthorizerOpts{
Authorizers: OperationAuthorizers{},
DefaultAuthorizer: DefaultAuthorizer,
}
}

// // Authenticate ...
// func Authenticate(opts ...AuthorizerOpt) openapi3filter.AuthenticationFunc {
// opts := DefaultAuthorizerOpts()

// return func(ctx context.Context, input *openapi3filter.AuthenticationInput) error {
// auth, ok := opts.Authorizers[input.SecuritySchemeName]
// if !ok {
// return opts.DefaultAuthorizer(ctx, input)
// }

// err := auth(ctx, input)
// if err != nil {
// return err
// }

// return auth(ctx, input)
// }
// }

0 comments on commit 363faaa

Please sign in to comment.