feat: organizations and API keys
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
|
||||
"gitlab.com/unboundsoftware/schemas/domain"
|
||||
"gitlab.com/unboundsoftware/schemas/hash"
|
||||
)
|
||||
|
||||
const (
|
||||
UserKey = ContextKey("user")
|
||||
OrganizationKey = ContextKey("organization")
|
||||
)
|
||||
|
||||
type Cache interface {
|
||||
OrganizationByAPIKey(apiKey string) *domain.Organization
|
||||
}
|
||||
|
||||
func NewAuth(cache Cache) *AuthMiddleware {
|
||||
return &AuthMiddleware{
|
||||
cache: cache,
|
||||
}
|
||||
}
|
||||
|
||||
type AuthMiddleware struct {
|
||||
cache Cache
|
||||
}
|
||||
|
||||
func (m *AuthMiddleware) Handler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
token, err := TokenFromContext(r.Context())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte("Invalid JWT token format"))
|
||||
return
|
||||
}
|
||||
if token != nil {
|
||||
ctx = context.WithValue(ctx, UserKey, token.Claims.(jwt.MapClaims)["sub"])
|
||||
}
|
||||
apiKey, err := ApiKeyFromContext(r.Context())
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte("Invalid API Key format"))
|
||||
return
|
||||
}
|
||||
if organization := m.cache.OrganizationByAPIKey(hash.String(apiKey)); organization != nil {
|
||||
ctx = context.WithValue(ctx, OrganizationKey, *organization)
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r.WithContext(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func UserFromContext(ctx context.Context) string {
|
||||
if value := ctx.Value(UserKey); value != nil {
|
||||
if u, ok := value.(string); ok {
|
||||
return u
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func OrganizationFromContext(ctx context.Context) string {
|
||||
if value := ctx.Value(OrganizationKey); value != nil {
|
||||
if u, ok := value.(domain.Organization); ok {
|
||||
return u.ID.String()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *AuthMiddleware) Directive(ctx context.Context, _ interface{}, next graphql.Resolver, user *bool, organization *bool) (res interface{}, err error) {
|
||||
if user != nil && *user {
|
||||
if u := UserFromContext(ctx); u == "" {
|
||||
return nil, fmt.Errorf("no user available in request")
|
||||
}
|
||||
}
|
||||
if organization != nil && *organization {
|
||||
if orgId := OrganizationFromContext(ctx); orgId == "" {
|
||||
return nil, fmt.Errorf("no organization available in request")
|
||||
}
|
||||
}
|
||||
return next(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user