From c5975a40228d8cde4ff716e2d4f51a3b5113d49b Mon Sep 17 00:00:00 2001 From: Andrey Melnikov Date: Tue, 10 Mar 2020 12:53:13 -0700 Subject: [PATCH] feat: Added auth token server issue #96 --- api/api.swagger.json | 275 +++++++++++------------------------------- api/auth.proto | 27 +++++ api/namespace.pb.go | 8 +- api/secret.pb.go | 8 +- api/workflow.pb.go | 8 +- go.mod | 2 +- main.go | 1 + pkg/namespace.go | 18 +++ server/auth_server.go | 43 +++++++ 9 files changed, 174 insertions(+), 216 deletions(-) create mode 100644 api/auth.proto create mode 100644 server/auth_server.go diff --git a/api/api.swagger.json b/api/api.swagger.json index d8b1f87..498288d 100644 --- a/api/api.swagger.json +++ b/api/api.swagger.json @@ -21,6 +21,32 @@ "application/json" ], "paths": { + "/apis/v1beta1/auth/token": { + "post": { + "operationId": "IsValidToken", + "responses": { + "200": { + "description": "A successful response.", + "schema": { + "$ref": "#/definitions/IsValidTokenResponse" + } + } + }, + "parameters": [ + { + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/Token" + } + } + ], + "tags": [ + "AuthService" + ] + } + }, "/apis/v1beta1/namespaces": { "get": { "operationId": "ListNamespaces", @@ -30,12 +56,6 @@ "schema": { "$ref": "#/definitions/ListNamespacesResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "tags": [ @@ -52,12 +72,6 @@ "schema": { "$ref": "#/definitions/ListSecretsResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -80,12 +94,6 @@ "schema": { "properties": {} } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -118,12 +126,6 @@ "schema": { "$ref": "#/definitions/Secret" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -152,12 +154,6 @@ "schema": { "$ref": "#/definitions/DeleteSecretResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -188,12 +184,6 @@ "schema": { "$ref": "#/definitions/SecretExistsResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -224,12 +214,6 @@ "schema": { "$ref": "#/definitions/AddSecretKeyValueResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -266,12 +250,6 @@ "schema": { "$ref": "#/definitions/UpdateSecretKeyValueResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -310,12 +288,6 @@ "schema": { "$ref": "#/definitions/DeleteSecretKeyResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -352,12 +324,6 @@ "schema": { "$ref": "#/definitions/ListWorkflowExecutionsResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -399,6 +365,7 @@ ] }, "post": { + "summary": "Creates a Workflow", "operationId": "CreateWorkflowExecution", "responses": { "200": { @@ -406,12 +373,6 @@ "schema": { "$ref": "#/definitions/WorkflowExecution" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -444,12 +405,6 @@ "schema": { "$ref": "#/definitions/WorkflowExecution" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -480,12 +435,6 @@ "schema": { "$ref": "#/definitions/ArtifactResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -522,12 +471,6 @@ "schema": { "$ref": "#/definitions/ListFilesResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -562,22 +505,7 @@ "200": { "description": "A successful response.(streaming responses)", "schema": { - "type": "object", - "properties": { - "result": { - "$ref": "#/definitions/LogEntry" - }, - "error": { - "$ref": "#/definitions/grpc.gateway.runtime.StreamError" - } - }, - "title": "Stream result of LogEntry" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" + "$ref": "#/x-stream-definitions/LogEntry" } } }, @@ -621,12 +549,6 @@ "schema": { "$ref": "#/definitions/GetWorkflowExecutionMetricsResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -663,12 +585,6 @@ "schema": { "$ref": "#/definitions/WorkflowExecution" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -699,12 +615,6 @@ "schema": { "properties": {} } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -733,22 +643,7 @@ "200": { "description": "A successful response.(streaming responses)", "schema": { - "type": "object", - "properties": { - "result": { - "$ref": "#/definitions/WorkflowExecution" - }, - "error": { - "$ref": "#/definitions/grpc.gateway.runtime.StreamError" - } - }, - "title": "Stream result of WorkflowExecution" - } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" + "$ref": "#/x-stream-definitions/WorkflowExecution" } } }, @@ -780,12 +675,6 @@ "schema": { "$ref": "#/definitions/ListWorkflowTemplatesResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -808,12 +697,6 @@ "schema": { "$ref": "#/definitions/WorkflowTemplate" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -846,12 +729,6 @@ "schema": { "$ref": "#/definitions/WorkflowTemplate" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -889,12 +766,6 @@ "schema": { "$ref": "#/definitions/ArchiveWorkflowTemplateResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -925,12 +796,6 @@ "schema": { "$ref": "#/definitions/ListWorkflowTemplateVersionsResponse" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -961,12 +826,6 @@ "schema": { "$ref": "#/definitions/WorkflowTemplate" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -1004,12 +863,6 @@ "schema": { "$ref": "#/definitions/WorkflowTemplate" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -1048,12 +901,6 @@ "schema": { "$ref": "#/definitions/WorkflowTemplate" } - }, - "default": { - "description": "An unexpected error response", - "schema": { - "$ref": "#/definitions/grpc.gateway.runtime.Error" - } } }, "parameters": [ @@ -1175,6 +1022,15 @@ } } }, + "IsValidTokenResponse": { + "type": "object", + "properties": { + "valid": { + "type": "boolean", + "format": "boolean" + } + } + }, "ListFilesResponse": { "type": "object", "properties": { @@ -1333,6 +1189,14 @@ } } }, + "Token": { + "type": "object", + "properties": { + "token": { + "type": "string" + } + } + }, "UpdateSecretKeyValueResponse": { "type": "object", "properties": { @@ -1432,27 +1296,6 @@ }, "description": "`Any` contains an arbitrary serialized protocol buffer message along with a\nURL that describes the type of the serialized message.\n\nProtobuf library provides support to pack/unpack Any values in the form\nof utility functions or additional generated methods of the Any type.\n\nExample 1: Pack and unpack a message in C++.\n\n Foo foo = ...;\n Any any;\n any.PackFrom(foo);\n ...\n if (any.UnpackTo(&foo)) {\n ...\n }\n\nExample 2: Pack and unpack a message in Java.\n\n Foo foo = ...;\n Any any = Any.pack(foo);\n ...\n if (any.is(Foo.class)) {\n foo = any.unpack(Foo.class);\n }\n\n Example 3: Pack and unpack a message in Python.\n\n foo = Foo(...)\n any = Any()\n any.Pack(foo)\n ...\n if any.Is(Foo.DESCRIPTOR):\n any.Unpack(foo)\n ...\n\n Example 4: Pack and unpack a message in Go\n\n foo := &pb.Foo{...}\n any, err := ptypes.MarshalAny(foo)\n ...\n foo := &pb.Foo{}\n if err := ptypes.UnmarshalAny(any, foo); err != nil {\n ...\n }\n\nThe pack methods provided by protobuf library will by default use\n'type.googleapis.com/full.type.name' as the type URL and the unpack\nmethods only use the fully qualified type name after the last '/'\nin the type URL, for example \"foo.bar.com/x/y.z\" will yield type\nname \"y.z\".\n\n\nJSON\n====\nThe JSON representation of an `Any` value uses the regular\nrepresentation of the deserialized, embedded message, with an\nadditional field `@type` which contains the type URL. Example:\n\n package google.profile;\n message Person {\n string first_name = 1;\n string last_name = 2;\n }\n\n {\n \"@type\": \"type.googleapis.com/google.profile.Person\",\n \"firstName\": ,\n \"lastName\": \n }\n\nIf the embedded message type is well-known and has a custom JSON\nrepresentation, that representation will be embedded adding a field\n`value` which holds the custom JSON in addition to the `@type`\nfield. Example (for message [google.protobuf.Duration][]):\n\n {\n \"@type\": \"type.googleapis.com/google.protobuf.Duration\",\n \"value\": \"1.212s\"\n }" }, - "grpc.gateway.runtime.Error": { - "type": "object", - "properties": { - "error": { - "type": "string" - }, - "code": { - "type": "integer", - "format": "int32" - }, - "message": { - "type": "string" - }, - "details": { - "type": "array", - "items": { - "$ref": "#/definitions/google.protobuf.Any" - } - } - } - }, "grpc.gateway.runtime.StreamError": { "type": "object", "properties": { @@ -1479,6 +1322,32 @@ } } }, + "x-stream-definitions": { + "LogEntry": { + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/LogEntry" + }, + "error": { + "$ref": "#/definitions/grpc.gateway.runtime.StreamError" + } + }, + "title": "Stream result of LogEntry" + }, + "WorkflowExecution": { + "type": "object", + "properties": { + "result": { + "$ref": "#/definitions/WorkflowExecution" + }, + "error": { + "$ref": "#/definitions/grpc.gateway.runtime.StreamError" + } + }, + "title": "Stream result of WorkflowExecution" + } + }, "securityDefinitions": { "Bearer": { "type": "apiKey", diff --git a/api/auth.proto b/api/auth.proto new file mode 100644 index 0000000..2025208 --- /dev/null +++ b/api/auth.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; + +package api; + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; + +service AuthService { + rpc IsValidToken(IsValidTokenRequest) returns (IsValidTokenResponse) { + option (google.api.http) = { + post: "/apis/v1beta1/auth/token" + body: "token" + }; + } +} + +message IsValidTokenResponse { + bool valid = 1; +} + +message IsValidTokenRequest { + Token token = 1; +} + +message Token { + string token = 1; +} \ No newline at end of file diff --git a/api/namespace.pb.go b/api/namespace.pb.go index 52b8e42..1b114ff 100644 --- a/api/namespace.pb.go +++ b/api/namespace.pb.go @@ -140,11 +140,11 @@ var fileDescriptor_ecb1e126f615f5dd = []byte{ // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConnInterface +var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 +const _ = grpc.SupportPackageIsVersion4 // NamespaceServiceClient is the client API for NamespaceService service. // @@ -154,10 +154,10 @@ type NamespaceServiceClient interface { } type namespaceServiceClient struct { - cc grpc.ClientConnInterface + cc *grpc.ClientConn } -func NewNamespaceServiceClient(cc grpc.ClientConnInterface) NamespaceServiceClient { +func NewNamespaceServiceClient(cc *grpc.ClientConn) NamespaceServiceClient { return &namespaceServiceClient{cc} } diff --git a/api/secret.pb.go b/api/secret.pb.go index e499817..971f47b 100644 --- a/api/secret.pb.go +++ b/api/secret.pb.go @@ -763,11 +763,11 @@ var fileDescriptor_6acf428160d7a216 = []byte{ // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConnInterface +var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 +const _ = grpc.SupportPackageIsVersion4 // SecretServiceClient is the client API for SecretService service. // @@ -784,10 +784,10 @@ type SecretServiceClient interface { } type secretServiceClient struct { - cc grpc.ClientConnInterface + cc *grpc.ClientConn } -func NewSecretServiceClient(cc grpc.ClientConnInterface) SecretServiceClient { +func NewSecretServiceClient(cc *grpc.ClientConn) SecretServiceClient { return &secretServiceClient{cc} } diff --git a/api/workflow.pb.go b/api/workflow.pb.go index cbc889a..3f35425 100644 --- a/api/workflow.pb.go +++ b/api/workflow.pb.go @@ -1159,11 +1159,11 @@ var fileDescriptor_892c7f566756b0be = []byte{ // Reference imports to suppress errors if they are not otherwise used. var _ context.Context -var _ grpc.ClientConnInterface +var _ grpc.ClientConn // This is a compile-time assertion to ensure that this generated file // is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion6 +const _ = grpc.SupportPackageIsVersion4 // WorkflowServiceClient is the client API for WorkflowService service. // @@ -1190,10 +1190,10 @@ type WorkflowServiceClient interface { } type workflowServiceClient struct { - cc grpc.ClientConnInterface + cc *grpc.ClientConn } -func NewWorkflowServiceClient(cc grpc.ClientConnInterface) WorkflowServiceClient { +func NewWorkflowServiceClient(cc *grpc.ClientConn) WorkflowServiceClient { return &workflowServiceClient{cc} } diff --git a/go.mod b/go.mod index 2432403..096ad76 100644 --- a/go.mod +++ b/go.mod @@ -34,7 +34,7 @@ require ( github.com/mattn/go-sqlite3 v2.0.2+incompatible // indirect github.com/minio/minio-go/v6 v6.0.45 github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/pkg/errors v0.9.1 // indirect + github.com/pkg/errors v0.9.1 github.com/pressly/goose v2.6.0+incompatible github.com/sirupsen/logrus v1.4.2 github.com/spf13/cobra v0.0.5 // indirect diff --git a/main.go b/main.go index 81decf9..99320d1 100644 --- a/main.go +++ b/main.go @@ -114,6 +114,7 @@ func startHTTPProxy() { registerHandler(api.RegisterWorkflowServiceHandlerFromEndpoint, ctx, mux, endpoint, opts) registerHandler(api.RegisterSecretServiceHandlerFromEndpoint, ctx, mux, endpoint, opts) registerHandler(api.RegisterNamespaceServiceHandlerFromEndpoint, ctx, mux, endpoint, opts) + registerHandler(api.RegisterAuthServiceHandlerFromEndpoint, ctx, mux, endpoint, opts) log.Printf("Starting HTTP proxy on port %v", *httpPort) diff --git a/pkg/namespace.go b/pkg/namespace.go index 8f87a45..9ee2592 100644 --- a/pkg/namespace.go +++ b/pkg/namespace.go @@ -8,6 +8,24 @@ import ( var onepanelEnabledLabelKey = "onepanel.io/enabled" +func (c *Client) ListOnepanelEnabledNamespaces() (namespaces []*Namespace, err error) { + namespaceList, err := c.CoreV1().Namespaces().List(metav1.ListOptions{ + LabelSelector: fmt.Sprintf("%s=%s", onepanelEnabledLabelKey, "true"), + }) + if err != nil { + return + } + + for _, ns := range namespaceList.Items { + namespaces = append(namespaces, &Namespace{ + Name: ns.Name, + Labels: ns.Labels, + }) + } + + return +} + func (c *Client) ListNamespaces() (namespaces []*Namespace, err error) { namespaceList, err := c.CoreV1().Namespaces().List(metav1.ListOptions{ LabelSelector: fmt.Sprintf("%s=%s", onepanelEnabledLabelKey, "true"), diff --git a/server/auth_server.go b/server/auth_server.go new file mode 100644 index 0000000..c4048ae --- /dev/null +++ b/server/auth_server.go @@ -0,0 +1,43 @@ +package server + +import ( + "context" + "github.com/onepanelio/core/api" + v1 "github.com/onepanelio/core/pkg" + "github.com/onepanelio/core/server/auth" + "github.com/pkg/errors" +) + +type AuthServer struct{} + +func NewAuthServer() *AuthServer { + return &AuthServer{} +} + +func (a *AuthServer) IsValidToken(ctx context.Context, req *api.IsValidTokenRequest) (*api.IsValidTokenResponse, error) { + client := ctx.Value("kubeClient").(*v1.Client) + + namespaces, err := client.ListOnepanelEnabledNamespaces() + if err != nil { + return nil, err + } + if len(namespaces) == 0 { + return nil, errors.New("No namespaces for onepanel setup.") + } + namespace := namespaces[0] + + allowed, err := auth.IsAuthorized(client, "", "get", "", "namespaces", namespace.Name) + if err != nil { + return nil, err + } + + if !allowed { + return &api.IsValidTokenResponse{ + Valid: false, + }, nil + } + + return &api.IsValidTokenResponse{ + Valid: true, + }, nil +}