diff --git a/charts/kubernetes-dashboard/templates/config/gateway.yaml b/charts/kubernetes-dashboard/templates/config/gateway.yaml index e43f963cb7de..b8f149d84cdb 100644 --- a/charts/kubernetes-dashboard/templates/config/gateway.yaml +++ b/charts/kubernetes-dashboard/templates/config/gateway.yaml @@ -37,6 +37,10 @@ data: paths: - /api/v1/csrftoken/login strip_path: false + - name: authMe + paths: + - /api/v1/me + strip_path: false - name: api host: {{ template "kubernetes-dashboard.fullname" . }}-{{ .Values.api.role }} port: 8000 diff --git a/hack/gateway/dev.kong.yml b/hack/gateway/dev.kong.yml index 6bd78432f6bc..2274f8f4cb28 100644 --- a/hack/gateway/dev.kong.yml +++ b/hack/gateway/dev.kong.yml @@ -28,6 +28,10 @@ services: paths: - /api/v1/csrftoken/login strip_path: false + - name: authMe + paths: + - /api/v1/me + strip_path: false - name: api host: api # Internal name of docker service port: 8000 diff --git a/hack/gateway/prod.kong.yml b/hack/gateway/prod.kong.yml index 18a0f05c3baf..99b698519f3f 100644 --- a/hack/gateway/prod.kong.yml +++ b/hack/gateway/prod.kong.yml @@ -28,6 +28,10 @@ services: paths: - /api/v1/csrftoken/login strip_path: false + - name: authMe + paths: + - /api/v1/me + strip_path: false - name: api host: api # Internal name of docker service port: 8000 diff --git a/modules/auth/go.mod b/modules/auth/go.mod index a85ba277fa07..7957d4148bb2 100644 --- a/modules/auth/go.mod +++ b/modules/auth/go.mod @@ -4,12 +4,14 @@ go 1.22.0 require ( github.com/gin-gonic/gin v1.9.1 + github.com/golang-jwt/jwt/v4 v4.5.0 github.com/spf13/pflag v1.0.5 golang.org/x/net v0.21.0 k8s.io/dashboard/client v0.0.0-00010101000000-000000000000 k8s.io/dashboard/csrf v0.0.0-00010101000000-000000000000 k8s.io/dashboard/errors v0.0.0-00010101000000-000000000000 k8s.io/dashboard/helpers v0.0.0-00010101000000-000000000000 + k8s.io/dashboard/types v0.0.0-00010101000000-000000000000 k8s.io/klog/v2 v2.120.1 ) @@ -64,7 +66,6 @@ require ( k8s.io/apiextensions-apiserver v0.29.2 // indirect k8s.io/apimachinery v0.29.2 // indirect k8s.io/client-go v0.29.2 // indirect - k8s.io/dashboard/types v0.0.0-00010101000000-000000000000 // indirect k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect diff --git a/modules/auth/go.sum b/modules/auth/go.sum index 84abb3255e96..ae702496ec7d 100644 --- a/modules/auth/go.sum +++ b/modules/auth/go.sum @@ -40,6 +40,8 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= diff --git a/modules/auth/main.go b/modules/auth/main.go index db652bdb7508..9a298c295b0e 100644 --- a/modules/auth/main.go +++ b/modules/auth/main.go @@ -27,6 +27,7 @@ import ( // Importing route packages forces route registration _ "k8s.io/dashboard/auth/pkg/routes/csrftoken" _ "k8s.io/dashboard/auth/pkg/routes/login" + _ "k8s.io/dashboard/auth/pkg/routes/me" ) func main() { diff --git a/modules/auth/pkg/routes/login/handler.go b/modules/auth/pkg/routes/login/handler.go index bd8668f22580..319f28a9eed4 100644 --- a/modules/auth/pkg/routes/login/handler.go +++ b/modules/auth/pkg/routes/login/handler.go @@ -26,7 +26,6 @@ import ( func init() { router.V1().POST("/login", handleLogin) - router.V1().GET("/login/status", handleLoginStatus) } func handleLogin(c *gin.Context) { @@ -47,7 +46,3 @@ func handleLogin(c *gin.Context) { c.JSON(code, response) } - -func handleLoginStatus(c *gin.Context) { - c.JSON(http.StatusOK, "OK") -} diff --git a/modules/auth/pkg/routes/me/handler.go b/modules/auth/pkg/routes/me/handler.go new file mode 100644 index 000000000000..4646cbbe8681 --- /dev/null +++ b/modules/auth/pkg/routes/me/handler.go @@ -0,0 +1,39 @@ +// Copyright 2017 The Kubernetes Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package login + +import ( + "net/http" + + "github.com/gin-gonic/gin" + "k8s.io/klog/v2" + + "k8s.io/dashboard/auth/pkg/router" +) + +func init() { + router.V1().GET("/me", handleMe) +} + +func handleMe(c *gin.Context) { + response, code, err := me(c.Request) + if err != nil { + klog.ErrorS(err, "Could not get user") + c.JSON(code, err) + return + } + + c.JSON(http.StatusOK, response) +} diff --git a/modules/auth/pkg/routes/me/me.go b/modules/auth/pkg/routes/me/me.go new file mode 100644 index 000000000000..991b89b598a7 --- /dev/null +++ b/modules/auth/pkg/routes/me/me.go @@ -0,0 +1,98 @@ +// Copyright 2017 The Kubernetes Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package login + +import ( + "bytes" + "encoding/json" + "net/http" + + "github.com/golang-jwt/jwt/v4" + + "k8s.io/dashboard/client" + "k8s.io/dashboard/errors" + "k8s.io/dashboard/types" +) + +const ( + tokenServiceAccountKey = "serviceaccount" +) + +type ServiceAccount struct { + Name string `json:"name"` + UID string `json:"uid"` +} + +func me(request *http.Request) (*types.User, int, error) { + k8sClient, err := client.Client(request) + if err != nil { + return nil, http.StatusInternalServerError, err + } + + // Make sure that authorization token is valid + if _, err = k8sClient.Discovery().ServerVersion(); err != nil { + code, err := errors.HandleError(err) + return nil, code, err + } + + return getUserFromToken(client.GetBearerToken(request)), http.StatusOK, nil +} + +func getUserFromToken(token string) *types.User { + parsed, _ := jwt.Parse(token, nil) + if parsed == nil { + return &types.User{Authenticated: true} + } + + claims := parsed.Claims.(jwt.MapClaims) + + found, value := traverse(tokenServiceAccountKey, claims) + if !found { + return &types.User{Authenticated: true} + } + + var sa ServiceAccount + ok := transcode(value, &sa) + if !ok { + return &types.User{Authenticated: true} + } + + return &types.User{Name: sa.Name, Authenticated: true} +} + +func traverse(key string, m map[string]interface{}) (found bool, value interface{}) { + for k, v := range m { + if k == key { + return true, v + } + + if innerMap, ok := v.(map[string]interface{}); ok { + return traverse(key, innerMap) + } + } + + return false, "" +} + +func transcode(in, out interface{}) bool { + buf := new(bytes.Buffer) + err := json.NewEncoder(buf).Encode(in) + if err != nil { + return false + } + + err = json.NewDecoder(buf).Decode(out) + return err == nil +} diff --git a/modules/common/types/common.go b/modules/common/types/common.go index 05df4f71ceb5..5ff8f2e8537d 100644 --- a/modules/common/types/common.go +++ b/modules/common/types/common.go @@ -82,3 +82,8 @@ func APIMappingByKind(kind ResourceKind) (apiMapping APIMapping, exists bool) { apiMapping, exists = kindToAPIMapping[kind] return } + +type User struct { + Name string `json:"name,omitempty"` + Authenticated bool `json:"authenticated"` +} diff --git a/modules/web/src/chrome/userpanel/component.ts b/modules/web/src/chrome/userpanel/component.ts index 275cad94423f..ba84198021a0 100644 --- a/modules/web/src/chrome/userpanel/component.ts +++ b/modules/web/src/chrome/userpanel/component.ts @@ -15,6 +15,7 @@ import {Component, ViewChild} from '@angular/core'; import {MatMenuTrigger} from '@angular/material/menu'; import {AuthService} from '@common/services/global/authentication'; +import {MeService} from "@common/services/global/me"; @Component({ selector: 'kd-user-panel', @@ -25,18 +26,12 @@ export class UserPanelComponent /* implements OnInit */ { @ViewChild(MatMenuTrigger) private readonly trigger_: MatMenuTrigger; - constructor(private readonly authService_: AuthService) {} + constructor(private readonly authService_: AuthService, private readonly _meService: MeService) {} get username(): string { - return ''; // todo + return this._meService.getUserName() } - // ngOnInit(): void { - // this.authService_.getLoginStatus().subscribe(status => { - // this.loginStatus = status; - // }); - // } - hasAuthHeader(): boolean { return this.authService_.hasAuthHeader(); } diff --git a/modules/web/src/chrome/userpanel/style.scss b/modules/web/src/chrome/userpanel/style.scss index f221968cc966..f66aabf901db 100644 --- a/modules/web/src/chrome/userpanel/style.scss +++ b/modules/web/src/chrome/userpanel/style.scss @@ -42,8 +42,8 @@ } .username { - font-size: $body-font-size-base; - margin-right: 2 * $baseline-grid; + font-size: $caption-font-size-base; + margin-top: .5 * $baseline-grid; } .method { diff --git a/modules/web/src/chrome/userpanel/template.html b/modules/web/src/chrome/userpanel/template.html index cea065f397a9..4bf7596477bc 100644 --- a/modules/web/src/chrome/userpanel/template.html +++ b/modules/web/src/chrome/userpanel/template.html @@ -30,15 +30,8 @@ fxFlex fxFlexAlign=" center" fxLayout="column" - class="method" + class="method kd-muted-light" > -
- {{ username }} -
Logged in with token + {{ username }}