diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml index ffc1ae7..43b3037 100644 --- a/.idea/dataSources.local.xml +++ b/.idea/dataSources.local.xml @@ -1,6 +1,6 @@ - + " diff --git a/.idea/workspace.xml b/.idea/workspace.xml index c69adac..cdbf3d5 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -4,16 +4,13 @@ - + + + + + - - - - - - - - @@ -175,7 +171,8 @@ - true diff --git a/pkg/hyper/auth.go b/pkg/hyper/auth.go new file mode 100644 index 0000000..2684317 --- /dev/null +++ b/pkg/hyper/auth.go @@ -0,0 +1,63 @@ +package hyper + +import ( + "context" + "fmt" + "git.solsynth.dev/hydrogen/passport/pkg/proto" + "google.golang.org/grpc" + "time" +) + +func (v *HyperConn) DoAuthenticate(atk, rtk string) (acc *proto.Userinfo, accessTk string, refreshTk string, err error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + var in *grpc.ClientConn + in, err = v.DiscoverServiceGRPC("Hydrogen.Passport") + if err != nil { + return + } + + var reply *proto.AuthReply + reply, err = proto.NewAuthClient(in).Authenticate(ctx, &proto.AuthRequest{ + AccessToken: atk, + RefreshToken: &rtk, + }) + if err != nil { + return + } + if reply != nil { + acc = reply.GetUserinfo() + accessTk = reply.GetAccessToken() + refreshTk = reply.GetRefreshToken() + if !reply.IsValid { + err = fmt.Errorf("invalid authorization context") + return + } + } + + return +} + +func (v *HyperConn) DoCheckPerm(atk string, key string, val []byte) error { + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + in, err := v.DiscoverServiceGRPC("Hydrogen.Passport") + if err != nil { + return err + } + + reply, err := proto.NewAuthClient(in).CheckPerm(ctx, &proto.CheckPermRequest{ + Token: atk, + Key: key, + Value: val, + }) + if err != nil { + return err + } else if !reply.GetIsValid() { + return fmt.Errorf("missing permission: %s", key) + } + + return nil +} diff --git a/pkg/hyper/auth_adaptor.go b/pkg/hyper/auth_adaptor.go new file mode 100644 index 0000000..d224f15 --- /dev/null +++ b/pkg/hyper/auth_adaptor.go @@ -0,0 +1,68 @@ +package hyper + +import ( + "git.solsynth.dev/hydrogen/passport/pkg/proto" + "github.com/gofiber/fiber/v2" + jsoniter "github.com/json-iterator/go" + "strings" + "time" +) + +const CookieAtk = "__hydrogen_atk" +const CookieRtk = "__hydrogen_rtk" + +func (v *HyperConn) AuthMiddleware(c *fiber.Ctx) error { + // Detect token + var atk string + if cookie := c.Cookies(CookieAtk); len(cookie) > 0 { + atk = cookie + } + if header := c.Get(fiber.HeaderAuthorization); len(header) > 0 { + tk := strings.Replace(header, "Bearer", "", 1) + atk = strings.TrimSpace(tk) + } + + c.Locals("p_token", atk) + + if user, newAtk, newRtk, err := v.DoAuthenticate(atk, c.Cookies(CookieRtk)); err == nil { + if newAtk != atk { + c.Cookie(&fiber.Cookie{ + Name: CookieAtk, + Value: newAtk, + SameSite: "Lax", + Expires: time.Now().Add(60 * time.Minute), + Path: "/", + }) + c.Cookie(&fiber.Cookie{ + Name: CookieRtk, + Value: newRtk, + SameSite: "Lax", + Expires: time.Now().Add(24 * 30 * time.Hour), + Path: "/", + }) + } + c.Locals("p_user", user) + return nil + } + + return c.Next() +} + +func (v *HyperConn) EnsureAuthenticated(c *fiber.Ctx) error { + if _, ok := c.Locals("p_user").(*proto.Userinfo); !ok { + return fiber.NewError(fiber.StatusUnauthorized) + } + + return nil +} + +func (v *HyperConn) EnsureGrantedPerm(c *fiber.Ctx, key string, val any) error { + if err := v.EnsureAuthenticated(c); err != nil { + return err + } + encodedVal, _ := jsoniter.Marshal(val) + if err := v.DoCheckPerm(c.Locals("p_token").(string), key, encodedVal); err != nil { + return fiber.NewError(fiber.StatusForbidden, err.Error()) + } + return nil +} diff --git a/pkg/hyper/conn.go b/pkg/hyper/conn.go new file mode 100644 index 0000000..a542baf --- /dev/null +++ b/pkg/hyper/conn.go @@ -0,0 +1,24 @@ +package hyper + +import ( + "fmt" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +type HyperConn struct { + Addr string +} + +func NewHyperConn(addr string) *HyperConn { + return &HyperConn{Addr: addr} +} + +func (v *HyperConn) DiscoverServiceGRPC(name string) (*grpc.ClientConn, error) { + target := fmt.Sprintf("consul://%s/%s", v.Addr, name) + return grpc.NewClient( + target, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`), + ) +} diff --git a/pkg/internal/gap/server.go b/pkg/internal/gap/server.go index 3a2e6b5..bcccd28 100644 --- a/pkg/internal/gap/server.go +++ b/pkg/internal/gap/server.go @@ -2,18 +2,17 @@ package gap import ( "fmt" + "strconv" + "strings" + "github.com/hashicorp/consul/api" "github.com/spf13/viper" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "strconv" - "strings" _ "github.com/mbobakov/grpc-consul-resolver" ) -var C *api.Client - func Register() error { cfg := api.DefaultConfig() cfg.Address = viper.GetString("consul.addr") @@ -41,17 +40,13 @@ func Register() error { DeregisterCriticalServiceAfter: "3m", } - if err := client.Agent().ServiceRegister(registration); err != nil { - return err - } else { - C = client - return nil - } + return client.Agent().ServiceRegister(registration) } func DiscoverPaperclip() (*grpc.ClientConn, error) { + target := fmt.Sprintf("consul://%s/Hydrogen.Paperclip", viper.GetString("consul.addr")) return grpc.NewClient( - "consul://127.0.0.1:8500/Hydrogen.Paperclip", + target, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`), )