Files
onepanel/server/auth/auth.go
2020-05-05 22:48:31 -07:00

154 lines
3.9 KiB
Go

package auth
import (
"context"
"errors"
"fmt"
"github.com/onepanelio/core/api"
"net/http"
"strings"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
v1 "github.com/onepanelio/core/pkg"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
authorizationv1 "k8s.io/api/authorization/v1"
)
func getBearerToken(ctx context.Context) (*string, bool) {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, false
}
prefix := "Bearer "
for _, t := range md.Get("authorization") {
if !strings.HasPrefix(t, prefix) {
return nil, false
}
t = strings.ReplaceAll(t, prefix, "")
return &t, true
}
for _, c := range md.Get("grpcgateway-cookie") {
header := http.Header{}
header.Add("Cookie", c)
req := &http.Request{
Header: header,
}
t, _ := req.Cookie("auth-token")
if t != nil {
return &t.Value, true
}
}
return nil, false
}
func getClient(ctx context.Context, kubeConfig *v1.Config, db *v1.DB) (context.Context, error) {
bearerToken, ok := getBearerToken(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, `Missing or invalid "authorization" header.`)
}
kubeConfig.BearerToken = *bearerToken
client, err := v1.NewClient(kubeConfig, db)
if err != nil {
return nil, err
}
return context.WithValue(ctx, "kubeClient", client), nil
}
func IsAuthorized(c *v1.Client, namespace, verb, group, resource, name string) (allowed bool, err error) {
review, err := c.AuthorizationV1().SelfSubjectAccessReviews().Create(&authorizationv1.SelfSubjectAccessReview{
Spec: authorizationv1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &authorizationv1.ResourceAttributes{
Namespace: namespace,
Verb: verb,
Group: group,
Resource: resource,
Name: name,
},
},
})
if err != nil {
return false, status.Error(codes.PermissionDenied, "Permission denied.")
}
allowed = review.Status.Allowed
if !allowed {
return false, status.Error(codes.PermissionDenied, "Permission denied.")
}
return
}
func UnaryInterceptor(kubeConfig *v1.Config, db *v1.DB) grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
if info.FullMethod == "/api.AuthService/IsValidToken" {
md, ok := metadata.FromIncomingContext(ctx)
if !ok {
return resp, errors.New("unable to get metadata from incoming context")
}
tokenRequest, ok := req.(*api.IsValidTokenRequest)
if !ok {
return resp, errors.New("IsValidToken does not have correct request type")
}
md.Set("authorization", tokenRequest.Token.Token)
ctx, err = getClient(ctx, kubeConfig, db)
if err != nil {
ctx = nil
}
return handler(ctx, req)
}
// if you don't need the token,
if info.FullMethod == "/api.AuthService/IsWorkspaceAuthenticated" {
md, ok := metadata.FromIncomingContext(ctx)
fmt.Printf("%+v\n", md) //todo remove
if !ok {
ctx = nil
return handler(ctx, req)
}
xOriginalAuthority := md.Get("x-original-authority")[0]
fqdn := md.Get("fqdn")[0]
//expected format: https://nginx-0--default.test-0.onepanel.site/
if xOriginalAuthority != fqdn { //Ignore fully qualified domain uris
ctx, err = getClient(ctx, kubeConfig, db)
if err != nil {
return
}
return handler(ctx, req)
}
}
// This guy checks for the token
ctx, err = getClient(ctx, kubeConfig, db)
if err != nil {
return
}
return handler(ctx, req)
}
}
func StreamingInterceptor(kubeConfig *v1.Config, db *v1.DB) grpc.StreamServerInterceptor {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) (err error) {
ctx, err := getClient(ss.Context(), kubeConfig, db)
if err != nil {
return
}
wrapped := grpc_middleware.WrapServerStream(ss)
wrapped.WrappedContext = ctx
return handler(srv, wrapped)
}
}