未验证 提交 cb596bcd 编写于 作者: S Sebastian Florek 提交者: GitHub

Add option to restrict access to certain page based on user permissions (#2639)

* Add option to restrict access to certain page based on user permissions

* Add documentation

* Add license headers

* Add test

* Generate translations

* Fix annotations

* Fix tests

* Adjust warning popup on login screen

* Fix serve:prod
上级 84ee355e
......@@ -419,6 +419,8 @@
<translation id="8546806166673222480" key="MSG_EVENTS_CARDLIST_6" desc="Label \'Last seen\' for the respective column of the events table (events list page).">Last seen</translation>
<translation id="8384910280515978185" key="MSG_EVENTS_CARDLIST_8" desc="User help on the events page when no events are to be displayed.">It is possible that all events have expired.</translation>
<translation id="4919061456654775514" key="MSG_EVENTS_WARNING_LABEL" desc="Label 'Warning' for the event selection drop-down.">Warning</translation>
<translation id="6870755296074399906" key="MSG_FORBIDDEN_ERROR" desc="Text shown on internal error page when user is forbidden to access some page.">You do not have required permissions to access this page.</translation>
<translation id="2570930006862369513" key="MSG_FORBIDDEN_TITLE_ERROR" desc="Title shown on internal error page when user is forbidden to access some page.">Forbidden</translation>
<translation id="339987725992094148" key="MSG_GRAPH_CPU_AXIS_LABEL" desc="Name of Y axis showing CPU usage.">CPU (cores)</translation>
<translation id="4576303150557908117" key="MSG_GRAPH_CPU_LIMIT_LEGEND_LABEL" desc="Name of the CPU limit metric as displayed in the legend.">CPU Limit</translation>
<translation id="6172878964647554384" key="MSG_GRAPH_CPU_USAGE_LEGEND_LABEL" desc="Name of the CPU usage metric as displayed in the legend.">CPU Usage</translation>
......
......@@ -423,6 +423,8 @@
<translation id="8546806166673222480" key="MSG_EVENTS_CARDLIST_6" desc="Label \'Last seen\' for the respective column of the events table (events list page).">直近</translation>
<translation id="8384910280515978185" key="MSG_EVENTS_CARDLIST_8" desc="User help on the events page when no events are to be displayed.">すべてのイベントが期限切れの可能性があります。</translation>
<translation id="4919061456654775514" key="MSG_EVENTS_WARNING_LABEL" desc="Label 'Warning' for the event selection drop-down.">警告</translation>
<translation id="6870755296074399906" key="MSG_FORBIDDEN_ERROR" desc="Text shown on internal error page when user is forbidden to access some page.">You do not have required permissions to access this page.</translation>
<translation id="2570930006862369513" key="MSG_FORBIDDEN_TITLE_ERROR" desc="Title shown on internal error page when user is forbidden to access some page.">Forbidden</translation>
<translation id="339987725992094148" key="MSG_GRAPH_CPU_AXIS_LABEL" desc="Name of Y axis showing CPU usage.">CPU (コア数)</translation>
<translation id="4576303150557908117" key="MSG_GRAPH_CPU_LIMIT_LEGEND_LABEL" desc="Name of the CPU usage metric as displayed in the legend.">CPU上限</translation>
<translation id="6172878964647554384" key="MSG_GRAPH_CPU_USAGE_LEGEND_LABEL" desc="Name of the CPU usage metric as displayed in the legend.">CPU使用量</translation>
......
......@@ -417,6 +417,8 @@
<translation id="8546806166673222480" key="MSG_EVENTS_CARDLIST_6" desc="Label \'Last seen\' for the respective column of the events table (events list page).">最近出現於</translation>
<translation id="8384910280515978185" key="MSG_EVENTS_CARDLIST_8" desc="User help on the events page when no events are to be displayed.">全部事件可能已過期</translation>
<translation id="4919061456654775514" key="MSG_EVENTS_WARNING_LABEL" desc="Label 'Warning' for the event selection drop-down.">警告</translation>
<translation id="6870755296074399906" key="MSG_FORBIDDEN_ERROR" desc="Text shown on internal error page when user is forbidden to access some page.">You do not have required permissions to access this page.</translation>
<translation id="2570930006862369513" key="MSG_FORBIDDEN_TITLE_ERROR" desc="Title shown on internal error page when user is forbidden to access some page.">Forbidden</translation>
<translation id="339987725992094148" key="MSG_GRAPH_CPU_AXIS_LABEL" desc="Name of Y axis showing CPU usage.">CPU (核心)</translation>
<translation id="4576303150557908117" key="MSG_GRAPH_CPU_LIMIT_LEGEND_LABEL" desc="Name of the CPU limit metric as displayed in the legend.">CPU 限制值</translation>
<translation id="6172878964647554384" key="MSG_GRAPH_CPU_USAGE_LEGEND_LABEL" desc="Name of the CPU usage metric as displayed in the legend.">CPU 使用量</translation>
......
......@@ -419,6 +419,8 @@
<translation id="8546806166673222480" key="MSG_EVENTS_CARDLIST_6" desc="Label \'Last seen\' for the respective column of the events table (events list page).">最近出现于</translation>
<translation id="8384910280515978185" key="MSG_EVENTS_CARDLIST_8" desc="User help on the events page when no events are to be displayed.">可能所有事件已过期</translation>
<translation id="4919061456654775514" key="MSG_EVENTS_WARNING_LABEL" desc="Label 'Warning' for the event selection drop-down.">警告</translation>
<translation id="6870755296074399906" key="MSG_FORBIDDEN_ERROR" desc="Text shown on internal error page when user is forbidden to access some page.">You do not have required permissions to access this page.</translation>
<translation id="2570930006862369513" key="MSG_FORBIDDEN_TITLE_ERROR" desc="Title shown on internal error page when user is forbidden to access some page.">Forbidden</translation>
<translation id="339987725992094148" key="MSG_GRAPH_CPU_AXIS_LABEL" desc="Name of Y axis showing CPU usage.">CPU (核)</translation>
<translation id="4576303150557908117" key="MSG_GRAPH_CPU_LIMIT_LEGEND_LABEL" desc="Name of the CPU limit metric as displayed in the legend.">CPU 限制值</translation>
<translation id="6172878964647554384" key="MSG_GRAPH_CPU_USAGE_LEGEND_LABEL" desc="Name of the CPU usage metric as displayed in the legend.">CPU 使用率</translation>
......
......@@ -111,6 +111,8 @@ func (self *rsaKeyHolder) recreate(obj runtime.Object) {
}
func (self *rsaKeyHolder) init() {
self.initEncryptionKey()
// Register event handlers
self.synchronizer.RegisterActionHandler(self.update, watch.Added, watch.Modified)
self.synchronizer.RegisterActionHandler(self.recreate, watch.Deleted)
......@@ -122,9 +124,6 @@ func (self *rsaKeyHolder) init() {
return
}
// If secret with key was not found generate new key
self.initEncryptionKey()
// Try to save generated key in a secret
log.Printf("Storing encryption key in a secret")
err := self.synchronizer.Create(self.getEncryptionKeyHolder())
......
......@@ -18,7 +18,7 @@ import (
"errors"
authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
kdErrors "github.com/kubernetes/dashboard/src/app/backend/errors"
"k8s.io/client-go/tools/clientcmd/api"
)
......@@ -26,7 +26,7 @@ import (
// Implements AuthManager interface
type authManager struct {
tokenManager authApi.TokenManager
clientManager client.ClientManager
clientManager clientapi.ClientManager
authenticationModes authApi.AuthenticationModes
}
......@@ -90,7 +90,7 @@ func (self authManager) healthCheck(authInfo api.AuthInfo) error {
}
// NewAuthManager creates auth manager.
func NewAuthManager(clientManager client.ClientManager, tokenManager authApi.TokenManager,
func NewAuthManager(clientManager clientapi.ClientManager, tokenManager authApi.TokenManager,
authenticationModes authApi.AuthenticationModes) authApi.AuthManager {
return &authManager{
tokenManager: tokenManager,
......
......@@ -24,6 +24,8 @@ import (
restful "github.com/emicklei/go-restful"
authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"k8s.io/api/authorization/v1"
k8sErrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
......@@ -66,8 +68,13 @@ func (self *fakeClientManager) HasAccess(authInfo api.AuthInfo) error {
return self.HasAccessError
}
func (self *fakeClientManager) VerberClient(req *restful.Request) (client.ResourceVerber, error) {
return client.ResourceVerber{}, nil
func (self *fakeClientManager) VerberClient(req *restful.Request) (clientapi.ResourceVerber, error) {
return client.NewResourceVerber(nil, nil, nil, nil, nil,
nil, nil), nil
}
func (self *fakeClientManager) CanI(req *restful.Request, ssar *v1.SelfSubjectAccessReview) bool {
return true
}
type fakeTokenManager struct {
......@@ -95,7 +102,7 @@ func TestAuthManager_Login(t *testing.T) {
cases := []struct {
info string
spec *authApi.LoginSpec
cManager client.ClientManager
cManager clientapi.ClientManager
tManager authApi.TokenManager
expected *authApi.AuthResponse
expectedErr error
......
// 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 api
import "k8s.io/api/authorization/v1"
// ToSelfSubjectAccessReview creates kubernetes API object based on provided data.
func ToSelfSubjectAccessReview(namespace, name, resource, verb string) *v1.SelfSubjectAccessReview {
return &v1.SelfSubjectAccessReview{
Spec: v1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &v1.ResourceAttributes{
Namespace: namespace,
Name: name,
Resource: resource,
Verb: verb,
},
},
}
}
// 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 api_test
import (
"reflect"
"testing"
"github.com/kubernetes/dashboard/src/app/backend/client/api"
"k8s.io/api/authorization/v1"
)
func TestToSelfSubjectAccessReview(t *testing.T) {
ns := "test-ns"
name := "test-name"
resourceName := "deployment"
verb := "GET"
expected := &v1.SelfSubjectAccessReview{
Spec: v1.SelfSubjectAccessReviewSpec{
ResourceAttributes: &v1.ResourceAttributes{
Namespace: ns,
Name: name,
Resource: resourceName,
Verb: verb,
},
},
}
got := api.ToSelfSubjectAccessReview(ns, name, resourceName, verb)
if !reflect.DeepEqual(got, expected) {
t.Fatalf("Expected to get %+v but got %+v", expected, got)
}
}
// 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 api
import (
"github.com/emicklei/go-restful"
authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api"
"k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
)
// ClientManager is responsible for initializing and creating clients to communicate with
// kubernetes apiserver on demand.
type ClientManager interface {
Client(req *restful.Request) (kubernetes.Interface, error)
InsecureClient() kubernetes.Interface
CanI(req *restful.Request, ssar *v1.SelfSubjectAccessReview) bool
Config(req *restful.Request) (*rest.Config, error)
ClientCmdConfig(req *restful.Request) (clientcmd.ClientConfig, error)
CSRFKey() string
HasAccess(authInfo api.AuthInfo) error
VerberClient(req *restful.Request) (ResourceVerber, error)
SetTokenManager(manager authApi.TokenManager)
}
// ResourceVerber is responsible for performing generic CRUD operations on all supported resources.
type ResourceVerber interface {
Put(kind string, namespaceSet bool, namespace string, name string,
object *runtime.Unknown) error
Get(kind string, namespaceSet bool, namespace string, name string) (runtime.Object, error)
Delete(kind string, namespaceSet bool, namespace string, name string) error
}
// CanIResponse is used to as response to check whether or not user is allowed to access given endpoint.
type CanIResponse struct {
Allowed bool `json:"allowed"`
}
......@@ -20,8 +20,10 @@ import (
"log"
"strings"
restful "github.com/emicklei/go-restful"
"github.com/emicklei/go-restful"
authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"k8s.io/api/authorization/v1"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
......@@ -49,19 +51,6 @@ const (
// VERSION of this binary
var Version = "UNKNOWN"
// ClientManager is responsible for initializing and creating clients to communicate with
// kubernetes apiserver on demand
type ClientManager interface {
Client(req *restful.Request) (kubernetes.Interface, error)
InsecureClient() kubernetes.Interface
Config(req *restful.Request) (*rest.Config, error)
ClientCmdConfig(req *restful.Request) (clientcmd.ClientConfig, error)
CSRFKey() string
HasAccess(authInfo api.AuthInfo) error
VerberClient(req *restful.Request) (ResourceVerber, error)
SetTokenManager(manager authApi.TokenManager)
}
// clientManager implements ClientManager interface
type clientManager struct {
// Autogenerated key on backend start used to secure requests from csrf attacks
......@@ -103,6 +92,28 @@ func (self *clientManager) InsecureClient() kubernetes.Interface {
return self.insecureClient
}
// CanI returns true when user is allowed to access data provided within SelfSubjectAccessReview, false otherwise.
func (self *clientManager) CanI(req *restful.Request, ssar *v1.SelfSubjectAccessReview) bool {
// In case user is not authenticated (uses skip option) do not allow access.
if info, _ := self.extractAuthInfo(req); info == nil {
return false
}
client, err := self.Client(req)
if err != nil {
log.Println(err)
return false
}
response, err := client.AuthorizationV1().SelfSubjectAccessReviews().Create(ssar)
if err != nil {
log.Println(err)
return false
}
return response.Status.Allowed
}
// Config creates rest Config based on authentication information extracted from request.
// Currently request header is only checked for existence of 'Authentication: BearerToken'
func (self *clientManager) Config(req *restful.Request) (*rest.Config, error) {
......@@ -171,10 +182,10 @@ func (self *clientManager) HasAccess(authInfo api.AuthInfo) error {
}
// VerberClient returns new verber client based on authentication information extracted from request
func (self *clientManager) VerberClient(req *restful.Request) (ResourceVerber, error) {
func (self *clientManager) VerberClient(req *restful.Request) (clientapi.ResourceVerber, error) {
client, err := self.Client(req)
if err != nil {
return ResourceVerber{}, err
return nil, err
}
return NewResourceVerber(client.CoreV1().RESTClient(),
......@@ -345,7 +356,7 @@ func (self *clientManager) isRunningInCluster() bool {
// NewClientManager creates client manager based on kubeConfigPath and apiserverHost parameters.
// If both are empty then in-cluster config is used.
func NewClientManager(kubeConfigPath, apiserverHost string) ClientManager {
func NewClientManager(kubeConfigPath, apiserverHost string) clientapi.ClientManager {
result := &clientManager{
kubeConfigPath: kubeConfigPath,
apiserverHost: apiserverHost,
......
......@@ -18,14 +18,15 @@ import (
"fmt"
"github.com/kubernetes/dashboard/src/app/backend/api"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
restclient "k8s.io/client-go/rest"
)
// ResourceVerber is a struct responsible for doing common verb operations on resources, like
// resourceVerber is a struct responsible for doing common verb operations on resources, like
// DELETE, PUT, UPDATE.
type ResourceVerber struct {
type resourceVerber struct {
client RESTClient
extensionsClient RESTClient
appsClient RESTClient
......@@ -35,7 +36,7 @@ type ResourceVerber struct {
storageClient RESTClient
}
func (verber *ResourceVerber) getRESTClientByType(clientType api.ClientType) RESTClient {
func (verber *resourceVerber) getRESTClientByType(clientType api.ClientType) RESTClient {
switch clientType {
case api.ClientTypeExtensionClient:
return verber.extensionsClient
......@@ -63,12 +64,13 @@ type RESTClient interface {
// NewResourceVerber creates a new resource verber that uses the given client for performing operations.
func NewResourceVerber(client, extensionsClient, appsClient,
batchClient, betaBatchClient, autoscalingClient, storageClient RESTClient) ResourceVerber {
return ResourceVerber{client, extensionsClient, appsClient, batchClient, betaBatchClient, autoscalingClient, storageClient}
batchClient, betaBatchClient, autoscalingClient, storageClient RESTClient) clientapi.ResourceVerber {
return &resourceVerber{client, extensionsClient, appsClient,
batchClient, betaBatchClient, autoscalingClient, storageClient}
}
// Delete deletes the resource of the given kind in the given namespace with the given name.
func (verber *ResourceVerber) Delete(kind string, namespaceSet bool, namespace string, name string) error {
func (verber *resourceVerber) Delete(kind string, namespaceSet bool, namespace string, name string) error {
resourceSpec, ok := api.KindToAPIMapping[kind]
if !ok {
return fmt.Errorf("Unknown resource kind: %s", kind)
......@@ -100,7 +102,7 @@ func (verber *ResourceVerber) Delete(kind string, namespaceSet bool, namespace s
}
// Put puts new resource version of the given kind in the given namespace with the given name.
func (verber *ResourceVerber) Put(kind string, namespaceSet bool, namespace string, name string,
func (verber *resourceVerber) Put(kind string, namespaceSet bool, namespace string, name string,
object *runtime.Unknown) error {
resourceSpec, ok := api.KindToAPIMapping[kind]
......@@ -132,7 +134,7 @@ func (verber *ResourceVerber) Put(kind string, namespaceSet bool, namespace stri
}
// Get gets the resource of the given kind in the given namespace with the given name.
func (verber *ResourceVerber) Get(kind string, namespaceSet bool, namespace string, name string) (runtime.Object, error) {
func (verber *resourceVerber) Get(kind string, namespaceSet bool, namespace string, name string) (runtime.Object, error) {
resourceSpec, ok := api.KindToAPIMapping[kind]
if !ok {
return nil, fmt.Errorf("Unknown resource kind: %s", kind)
......
......@@ -68,7 +68,7 @@ func (c *FakeRESTClient) Get() *restclient.Request {
}
func TestDeleteShouldPropagateErrorsAndChoseClient(t *testing.T) {
verber := ResourceVerber{
verber := resourceVerber{
client: &FakeRESTClient{err: errors.New("err")},
extensionsClient: &FakeRESTClient{err: errors.New("err from extensions")},
appsClient: &FakeRESTClient{err: errors.New("err from apps")},
......@@ -94,7 +94,7 @@ func TestDeleteShouldPropagateErrorsAndChoseClient(t *testing.T) {
}
func TestGetShouldPropagateErrorsAndChoseClient(t *testing.T) {
verber := ResourceVerber{
verber := resourceVerber{
client: &FakeRESTClient{err: errors.New("err")},
extensionsClient: &FakeRESTClient{err: errors.New("err from extensions")},
appsClient: &FakeRESTClient{err: errors.New("err from apps")},
......@@ -120,7 +120,7 @@ func TestGetShouldPropagateErrorsAndChoseClient(t *testing.T) {
}
func TestDeleteShouldThrowErrorOnUnknownResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
err := verber.Delete("foo", true, "bar", "baz")
......@@ -130,7 +130,7 @@ func TestDeleteShouldThrowErrorOnUnknownResourceKind(t *testing.T) {
}
func TestGetShouldThrowErrorOnUnknownResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
_, err := verber.Get("foo", true, "bar", "baz")
......@@ -140,7 +140,7 @@ func TestGetShouldThrowErrorOnUnknownResourceKind(t *testing.T) {
}
func TestPutShouldThrowErrorOnUnknownResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
err := verber.Put("foo", false, "", "baz", nil)
......@@ -150,7 +150,7 @@ func TestPutShouldThrowErrorOnUnknownResourceKind(t *testing.T) {
}
func TestGetShouldRespectNamespacednessOfResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
_, err := verber.Get("service", false, "", "baz")
......@@ -160,7 +160,7 @@ func TestGetShouldRespectNamespacednessOfResourceKind(t *testing.T) {
}
func TestPutShouldRespectNamespacednessOfResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
err := verber.Put("service", false, "", "baz", nil)
......@@ -170,7 +170,7 @@ func TestPutShouldRespectNamespacednessOfResourceKind(t *testing.T) {
}
func TestDeleteShouldRespectNamespacednessOfResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
err := verber.Delete("service", false, "", "baz")
......@@ -180,7 +180,7 @@ func TestDeleteShouldRespectNamespacednessOfResourceKind(t *testing.T) {
}
func TestGetShouldRespectNotNamespacednessOfResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
_, err := verber.Get("namespace", true, "bar", "baz")
......@@ -190,7 +190,7 @@ func TestGetShouldRespectNotNamespacednessOfResourceKind(t *testing.T) {
}
func TestPutShouldRespectNotNamespacednessOfResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
err := verber.Put("namespace", true, "bar", "baz", nil)
......@@ -200,7 +200,7 @@ func TestPutShouldRespectNotNamespacednessOfResourceKind(t *testing.T) {
}
func TestDeleteShouldRespectNotNamespacednessOfResourceKind(t *testing.T) {
verber := ResourceVerber{client: &FakeRESTClient{}}
verber := resourceVerber{client: &FakeRESTClient{}}
err := verber.Delete("namespace", true, "bar", "baz")
......
......@@ -30,6 +30,7 @@ import (
"github.com/kubernetes/dashboard/src/app/backend/cert"
"github.com/kubernetes/dashboard/src/app/backend/cert/ecdsa"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"github.com/kubernetes/dashboard/src/app/backend/handler"
"github.com/kubernetes/dashboard/src/app/backend/integration"
integrationapi "github.com/kubernetes/dashboard/src/app/backend/integration/api"
......@@ -146,7 +147,7 @@ func main() {
select {}
}
func initAuthManager(clientManager client.ClientManager, tokenTTL time.Duration) authApi.AuthManager {
func initAuthManager(clientManager clientapi.ClientManager, tokenTTL time.Duration) authApi.AuthManager {
insecureClient := clientManager.InsecureClient()
// Init default encryption key synchronizer
......
......@@ -24,7 +24,7 @@ import (
"github.com/kubernetes/dashboard/src/app/backend/api"
"github.com/kubernetes/dashboard/src/app/backend/auth"
authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"github.com/kubernetes/dashboard/src/app/backend/integration"
metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api"
"github.com/kubernetes/dashboard/src/app/backend/resource/cluster"
......@@ -78,21 +78,21 @@ const (
ResponseLogString = "[%s] Outcoming response to %s with %d status code"
)
// APIHandler is a representation of API handler. Structure contains client, Heapster client and client configuration.
// APIHandler is a representation of API handler. Structure contains clientapi, Heapster clientapi and clientapi configuration.
type APIHandler struct {
iManager integration.IntegrationManager
cManager client.ClientManager
cManager clientapi.ClientManager
sManager settings.SettingsManager
}
// TerminalResponse is sent by handleExecShell. The Id is a random session id that binds the original REST request and the SockJS connection.
// Any client in possession of this Id can hijack the terminal session.
// Any clientapi in possession of this Id can hijack the terminal session.
type TerminalResponse struct {
Id string `json:"id"`
}
// CreateHTTPAPIHandler creates a new HTTP handler that handles all requests to the API of the backend.
func CreateHTTPAPIHandler(iManager integration.IntegrationManager, cManager client.ClientManager,
func CreateHTTPAPIHandler(iManager integration.IntegrationManager, cManager clientapi.ClientManager,
authManager authApi.AuthManager, enableInsecureLogin bool, sManager settings.SettingsManager,
sbManager systembanner.SystemBannerManager) (
......
......@@ -25,7 +25,7 @@ import (
restful "github.com/emicklei/go-restful"
authApi "github.com/kubernetes/dashboard/src/app/backend/auth/api"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
kdErrors "github.com/kubernetes/dashboard/src/app/backend/errors"
"golang.org/x/net/xsrftoken"
errorsK8s "k8s.io/apimachinery/pkg/api/errors"
......@@ -33,7 +33,7 @@ import (
)
// InstallFilters installs defined filter for given web service
func InstallFilters(ws *restful.WebService, manager client.ClientManager) {
func InstallFilters(ws *restful.WebService, manager clientapi.ClientManager) {
ws.Filter(requestAndResponseLogger)
ws.Filter(metricsFilter)
ws.Filter(validateXSRFFilter(manager.CSRFKey()))
......
......@@ -17,7 +17,7 @@ package integration
import (
"fmt"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"github.com/kubernetes/dashboard/src/app/backend/integration/api"
"github.com/kubernetes/dashboard/src/app/backend/integration/metric"
"k8s.io/apimachinery/pkg/apis/meta/v1"
......@@ -59,14 +59,14 @@ func (self *integrationManager) getState(integration api.Integration) *api.Integ
Error: integration.HealthCheck(),
}
result.Connected = (result.Error == nil)
result.Connected = result.Error == nil
result.LastChecked = v1.Now()
return result
}
// NewIntegrationManager creates integration manager.
func NewIntegrationManager(manager client.ClientManager) IntegrationManager {
func NewIntegrationManager(manager clientapi.ClientManager) IntegrationManager {
return &integrationManager{
metric: metric.NewMetricManager(manager),
}
......
......@@ -19,7 +19,7 @@ import (
"log"
"time"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
integrationapi "github.com/kubernetes/dashboard/src/app/backend/integration/api"
metricapi "github.com/kubernetes/dashboard/src/app/backend/integration/metric/api"
"github.com/kubernetes/dashboard/src/app/backend/integration/metric/heapster"
......@@ -46,7 +46,7 @@ type MetricManager interface {
// Implements MetricManager interface.
type metricManager struct {
manager client.ClientManager
manager clientapi.ClientManager
clients map[integrationapi.IntegrationID]metricapi.MetricClient
active metricapi.MetricClient
}
......@@ -128,7 +128,7 @@ func (self *metricManager) ConfigureHeapster(host string) MetricManager {
}
// NewMetricManager creates metric manager.
func NewMetricManager(manager client.ClientManager) MetricManager {
func NewMetricManager(manager clientapi.ClientManager) MetricManager {
return &metricManager{
manager: manager,
clients: make(map[integrationapi.IntegrationID]metricapi.MetricClient),
......
......@@ -19,6 +19,7 @@ import (
"net/http"
restful "github.com/emicklei/go-restful"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"github.com/kubernetes/dashboard/src/app/backend/settings/api"
errorsK8s "k8s.io/apimachinery/pkg/api/errors"
)
......@@ -34,6 +35,9 @@ func (self *SettingsHandler) Install(ws *restful.WebService) {
ws.GET("/settings/global").
To(self.handleSettingsGlobalGet).
Writes(api.Settings{}))
ws.Route(ws.GET("/settings/global/cani").
To(self.handleSettingsGlobalCanI).
Writes(clientapi.CanIResponse{}))
ws.Route(
ws.PUT("/settings/global").
To(self.handleSettingsGlobalSave).
......@@ -41,6 +45,22 @@ func (self *SettingsHandler) Install(ws *restful.WebService) {
Writes(api.Settings{}))
}
func (self *SettingsHandler) handleSettingsGlobalCanI(request *restful.Request, response *restful.Response) {
verb := request.QueryParameter("verb")
if len(verb) == 0 {
verb = http.MethodGet
}
canI := self.manager.clientManager.CanI(request, clientapi.ToSelfSubjectAccessReview(
api.SettingsConfigMapNamespace,
api.SettingsConfigMapName,
api.ConfigMapKindName,
verb,
))
response.WriteHeaderAndEntity(http.StatusOK, clientapi.CanIResponse{canI})
}
func (self *SettingsHandler) handleSettingsGlobalGet(request *restful.Request, response *restful.Response) {
client, err := self.manager.clientManager.Client(request)
if err != nil {
......
......@@ -19,7 +19,7 @@ import (
"log"
"reflect"
"github.com/kubernetes/dashboard/src/app/backend/client"
clientapi "github.com/kubernetes/dashboard/src/app/backend/client/api"
"github.com/kubernetes/dashboard/src/app/backend/settings/api"
"k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
......@@ -30,11 +30,11 @@ import (
type SettingsManager struct {
settings map[string]api.Settings
rawSettings map[string]string
clientManager client.ClientManager
clientManager clientapi.ClientManager
}
// NewSettingsManager creates new settings manager.
func NewSettingsManager(clientManager client.ClientManager) SettingsManager {
func NewSettingsManager(clientManager clientapi.ClientManager) SettingsManager {
return SettingsManager{
settings: make(map[string]api.Settings),
clientManager: clientManager,
......
......@@ -18,7 +18,6 @@ import (
"time"
syncapi "github.com/kubernetes/dashboard/src/app/backend/sync/api"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
......@@ -30,7 +29,6 @@ import (
type SecretPoller struct {
name string
namespace string
secret *v1.Secret
client kubernetes.Interface
watcher *PollWatcher
}
......@@ -52,13 +50,9 @@ func (self *SecretPoller) Poll(interval time.Duration) watch.Interface {
}
// Gets secret from API server and transforms it to watch.Event object.
func (self *SecretPoller) getSecretEvent() watch.Event {
func (self *SecretPoller) getSecretEvent() (event watch.Event) {
secret, err := self.client.CoreV1().Secrets(self.namespace).Get(self.name, metav1.GetOptions{})
if secret != nil {
self.secret = secret
}
event := watch.Event{
event = watch.Event{
Object: secret,
Type: watch.Added,
}
......@@ -67,13 +61,12 @@ func (self *SecretPoller) getSecretEvent() watch.Event {
event.Type = watch.Error
}
if self.isNotFoundError(err) && self.secret != nil {
// In case it was never created we can still mark it as deleted and let secret be recreated.
if self.isNotFoundError(err) {
event.Type = watch.Deleted
event.Object = self.secret
self.secret = nil
}
return event
return
}
// Checks whether or not given error is a not found error (404).
......@@ -92,6 +85,5 @@ func NewSecretPoller(name, namespace string, client kubernetes.Interface) syncap
namespace: namespace,
client: client,
watcher: NewPollWatcher(),
secret: nil,
}
}
......@@ -1561,3 +1561,10 @@ backendApi.Status;
* }}
*/
backendApi.SystemBanner;
/**
* @typedef {{
* allowed: boolean
* }}
*/
backendApi.CanIResponse;
// 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.
import {stateName, StateParams} from '../../error/state';
import {kdLocalizedErrors} from '../errorhandling/errors';
/** @final */
export class AuthorizerService {
/**
* @param {!angular.$resource} $resource
* @param {!kdUiRouter.$state} $state
* @ngInject
*/
constructor($resource, $state) {
/** @private {!angular.$resource} */
this.resource_ = $resource;
/** @private {!kdUiRouter.$state} */
this.state_ = $state;
/** @private {string} */
this.authorizationSubUrl_ = '/cani';
}
/**
* @param {string} url
* @return {!angular.$q.Promise}
*/
proxyGET(url) {
return this.resource_(`${url}${this.authorizationSubUrl_}`)
.get()
.$promise.then(
(/** @type {!backendApi.CanIResponse} */ response) => {
if (response.allowed) {
return this.resource_(url).get().$promise;
}
this.state_.go(stateName, this.getAccessForbiddenError());
},
(err) => {
this.state_.go(stateName, new StateParams(err.detail, ''));
});
}
/** @return {!Object} */
getAccessForbiddenError() {
return {
error: {
statusText: kdLocalizedErrors.MSG_FORBIDDEN_TITLE_ERROR,
status: 403,
data: kdLocalizedErrors.MSG_FORBIDDEN_ERROR,
},
};
}
}
......@@ -13,6 +13,8 @@
// limitations under the License.
import csrftokenModule from '../csrftoken/module';
import {AuthorizerService} from './authorizer';
import {AuthInterceptor} from './interceptor';
import {AuthService} from './service';
......@@ -29,6 +31,7 @@ export default angular
csrftokenModule.name,
])
.service('kdAuthService', AuthService)
.service('kdAuthorizerService', AuthorizerService)
.factory('kdAuthInterceptor', AuthInterceptor.NewAuthInterceptor)
.config(initAuthInterceptor)
.constant('kdTokenCookieName', 'jweToken')
......
......@@ -52,6 +52,16 @@ export const kdLocalizedErrors = {
@export {kdError} @desc Text shown when saved token could not be decrypted by backend or it has expired.
*/
MSG_TOKEN_EXPIRED_ERROR: goog.getMsg('Session expired. Please log in again.'),
/**
* @export {kdError} @desc Text shown on internal error page when user is forbidden to access some page.
*/
MSG_FORBIDDEN_ERROR: goog.getMsg('You do not have required permissions to access this page.'),
/**
* @export {kdError} @desc Title shown on internal error page when user is forbidden to access some page.
*/
MSG_FORBIDDEN_TITLE_ERROR: goog.getMsg('Forbidden'),
};
/**
......
......@@ -47,6 +47,7 @@ kd-login {
.kd-login-error {
display: flex;
justify-content: center;
margin-bottom: 2 * $baseline-grid;
.kd-warnings-container {
width: 50%;
......
......@@ -53,12 +53,10 @@ const i18n = {
};
/**
* @param {!angular.$resource} $resource
* @param {!../common/auth/authorizer.AuthorizerService} kdAuthorizerService
* @return {!angular.$q.Promise}
* @ngInject
*/
function resolveGlobalSettings($resource) {
/** @type {!angular.Resource} */
let resource = $resource('api/v1/settings/global');
return resource.get().$promise;
function resolveGlobalSettings(kdAuthorizerService) {
return kdAuthorizerService.proxyGET('api/v1/settings/global');
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册