diff --git a/Makefile b/Makefile index 9e473613f..a36988a5b 100644 --- a/Makefile +++ b/Makefile @@ -19,7 +19,7 @@ unit-test-core: go test -coverprofile=coverage.txt $(shell go list ./pkg/... ./cmd/... ./apis/... | grep -v apiserver) go test $(shell go list ./references/... | grep -v apiserver) unit-test-apiserver: - go test -coverprofile=coverage.txt $(shell go list ./pkg/... ./cmd/... | grep -E 'apiserver|velaql') + go test -gcflags=all=-l -coverprofile=coverage.txt $(shell go list ./pkg/... ./cmd/... | grep -E 'apiserver|velaql') # Build vela cli binary build: fmt vet lint staticcheck vela-cli kubectl-vela diff --git a/go.mod b/go.mod index 80c0825b9..16e27645a 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 github.com/briandowns/spinner v1.11.1 github.com/containerd/containerd v1.4.13 + github.com/coreos/go-oidc v2.1.0+incompatible github.com/coreos/prometheus-operator v0.41.1 github.com/crossplane/crossplane-runtime v0.14.1-0.20210722005935-0b469fcc77cd github.com/davecgh/go-spew v1.1.1 @@ -214,6 +215,7 @@ require ( github.com/pelletier/go-toml v1.9.3 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect github.com/prometheus/procfs v0.6.0 // indirect @@ -260,6 +262,7 @@ require ( gopkg.in/gorp.v1 v1.7.2 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.62.0 // indirect + gopkg.in/square/go-jose.v2 v2.2.2 // indirect gopkg.in/src-d/go-billy.v4 v4.3.2 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/warnings.v0 v0.1.2 // indirect diff --git a/go.sum b/go.sum index 6c691e951..f05739d25 100644 --- a/go.sum +++ b/go.sum @@ -353,6 +353,7 @@ github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/etcd v3.3.15+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= @@ -1359,6 +1360,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polyfloyd/go-errorlint v0.0.0-20210510181950-ab96adb96fea/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021 h1:0XM1XL/OFFJjXsYXlG30spTkV/E9+gmd5GD1w2HE8xM= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus-community/prom-label-proxy v0.1.1-0.20200616110844-0fbfa11fa8f3/go.mod h1:XdjyZg7LCbCC5FADHtpgNp6kQ0W9beXVGfmcvndMj5Y= github.com/prometheus/alertmanager v0.18.0/go.mod h1:WcxHBl40VSPuOaqWae6l6HpnEOVRIycEJ7i9iYkadEE= @@ -2385,6 +2387,7 @@ gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2 h1:orlkJ3myw8CN1nVQHBFfloD+L3egixIa4FvUP6RosSA= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2 h1:0SQA1pRztfTFx2miS8sA97XvooFeNOmvUenF4o0EcVg= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= diff --git a/pkg/apiserver/model/system_info.go b/pkg/apiserver/model/system_info.go index a75c75717..639af53f9 100644 --- a/pkg/apiserver/model/system_info.go +++ b/pkg/apiserver/model/system_info.go @@ -20,11 +20,22 @@ func init() { RegistModel(&SystemInfo{}) } +// LoginType is the type of login +type LoginType string + +const ( + // LoginTypeDex is the dex login type + LoginTypeDex LoginType = "dex" + // LoginTypeLocal is the local login type + LoginTypeLocal LoginType = "local" +) + // SystemInfo systemInfo model type SystemInfo struct { BaseModel - InstallID string `json:"installID"` - EnableCollection bool `json:"enableCollection"` + InstallID string `json:"installID"` + EnableCollection bool `json:"enableCollection"` + LoginType LoginType `json:"loginType"` } // TableName return custom table name diff --git a/pkg/apiserver/model/user.go b/pkg/apiserver/model/user.go new file mode 100644 index 000000000..6b0fa99e5 --- /dev/null +++ b/pkg/apiserver/model/user.go @@ -0,0 +1,61 @@ +/* +Copyright 2022 The KubeVela 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 model + +import "strings" + +func init() { + RegistModel(&User{}) +} + +// User is the model of user +type User struct { + BaseModel + Name string `json:"name"` + Email string `json:"email"` + Alias string `json:"alias,omitempty"` + Password string `json:"password,omitempty"` + Disabled bool `json:"disabled"` +} + +// TableName return custom table name +func (u *User) TableName() string { + return tableNamePrefix + "user" +} + +// PrimaryKey return custom primary key +func (u *User) PrimaryKey() string { + return verifyUserValue(u.Name) +} + +// Index return custom index +func (u *User) Index() map[string]string { + index := make(map[string]string) + if u.Name != "" { + index["name"] = verifyUserValue(u.Name) + } + if u.Email != "" { + index["email"] = verifyUserValue(u.Email) + } + return index +} + +func verifyUserValue(v string) string { + s := strings.ReplaceAll(v, "@", "-") + s = strings.ReplaceAll(s, " ", "-") + return s +} diff --git a/pkg/apiserver/rest/apis/v1/types.go b/pkg/apiserver/rest/apis/v1/types.go index 371754a39..4226ce619 100644 --- a/pkg/apiserver/rest/apis/v1/types.go +++ b/pkg/apiserver/rest/apis/v1/types.go @@ -1080,3 +1080,25 @@ type ChartVersionListResponse struct { type SimpleResponse struct { Status string `json:"status"` } + +// LoginResponse is the response of login request +type LoginResponse struct { + UserInfo DetailUserResponse `json:"userInfo"` + AccessToken string `json:"accessToken,omitempty"` + RefreshToken string `json:"refreshToken,omitempty"` +} + +// DetailUserResponse is the detail user info for the response +type DetailUserResponse struct { + Name string `json:"name"` + Alias string `json:"alias,omitempty"` + Email string `json:"email"` +} + +// DexConfigResponse is the response of dex config +type DexConfigResponse struct { + ClientID string `json:"clientID"` + ClientSecret string `json:"clientSecret"` + RedirectURL string `json:"redirectURL"` + Issuer string `json:"issuer"` +} diff --git a/pkg/apiserver/rest/usecase/authentication.go b/pkg/apiserver/rest/usecase/authentication.go new file mode 100644 index 000000000..7295f4da7 --- /dev/null +++ b/pkg/apiserver/rest/usecase/authentication.go @@ -0,0 +1,204 @@ +/* +Copyright 2022 The KubeVela 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 usecase + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/coreos/go-oidc" + "github.com/emicklei/go-restful/v3" + "golang.org/x/oauth2" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + velatypes "github.com/oam-dev/kubevela/apis/types" + "github.com/oam-dev/kubevela/pkg/apiserver/clients" + "github.com/oam-dev/kubevela/pkg/apiserver/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/log" + "github.com/oam-dev/kubevela/pkg/apiserver/model" + apisv1 "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" +) + +const ( + secretDexConfig = "dex-config" +) + +// AuthenticationUsecase is the usecase of authentication +type AuthenticationUsecase interface { + Login(ctx context.Context, req *restful.Request) (*apisv1.LoginResponse, error) + GetDexConfig(ctx context.Context) (*apisv1.DexConfigResponse, error) +} + +type authenticationUsecaseImpl struct { + sysUsecase SystemInfoUsecase + ds datastore.DataStore + kubeClient client.Client +} + +// NewAuthenticationUsecase new authentication usecase +func NewAuthenticationUsecase(ds datastore.DataStore, sysUsecase SystemInfoUsecase) AuthenticationUsecase { + kubecli, err := clients.GetKubeClient() + if err != nil { + log.Logger.Fatalf("failed to get kube client: %s", err.Error()) + } + return &authenticationUsecaseImpl{ + sysUsecase: sysUsecase, + ds: ds, + kubeClient: kubecli, + } +} + +type authHandler interface { + login(ctx context.Context) (*apisv1.LoginResponse, error) +} + +type dexHandlerImpl struct { + token *oauth2.Token + idToken *oidc.IDToken + ds datastore.DataStore +} + +func (a *authenticationUsecaseImpl) newDexHandler(ctx context.Context, req *restful.Request) (*dexHandlerImpl, error) { + dexConfig, err := a.GetDexConfig(ctx) + if err != nil { + return nil, err + } + provider, err := oidc.NewProvider(ctx, dexConfig.Issuer) + if err != nil { + return nil, err + } + idTokenVerifier := provider.Verifier(&oidc.Config{ClientID: dexConfig.ClientID}) + code := req.HeaderParameter("code") + oauth2Config := &oauth2.Config{ + ClientID: dexConfig.ClientID, + ClientSecret: dexConfig.ClientSecret, + Endpoint: provider.Endpoint(), + RedirectURL: dexConfig.RedirectURL, + } + oidcCtx := oidc.ClientContext(ctx, http.DefaultClient) + token, err := oauth2Config.Exchange(oidcCtx, code) + if err != nil { + return nil, err + } + idToken, err := idTokenVerifier.Verify(ctx, token.Extra("id_token").(string)) + if err != nil { + return nil, err + } + return &dexHandlerImpl{ + token: token, + idToken: idToken, + ds: a.ds, + }, nil +} + +func (a *authenticationUsecaseImpl) Login(ctx context.Context, req *restful.Request) (*apisv1.LoginResponse, error) { + var handler authHandler + var err error + sysInfo, err := a.sysUsecase.GetSystemInfo(ctx) + if err != nil { + return nil, err + } + loginType := sysInfo.LoginType + + switch loginType { + case model.LoginTypeDex: + handler, err = a.newDexHandler(ctx, req) + if err != nil { + return nil, err + } + case model.LoginTypeLocal: + default: + return nil, bcode.ErrUnsupportedLoginType + } + return handler.login(ctx) +} + +func (a *authenticationUsecaseImpl) GetDexConfig(ctx context.Context) (*apisv1.DexConfigResponse, error) { + secret := &v1.Secret{} + if err := a.kubeClient.Get(ctx, types.NamespacedName{ + Name: secretDexConfig, + Namespace: velatypes.DefaultKubeVelaNS, + }, secret); err != nil { + log.Logger.Errorf("failed to get dex config: %s", err.Error()) + return nil, err + } + var config struct { + Issuer string `json:"issuer"` + StaticClients []struct { + ID string `json:"id"` + Secret string `json:"secret"` + RedirectURIs []string `json:"redirectURIs"` + } `json:"staticClients"` + } + if err := json.Unmarshal(secret.Data[secretDexConfig], &config); err != nil { + log.Logger.Errorf("failed to unmarshal dex config: %s", err.Error()) + return nil, err + } + if len(config.StaticClients) < 1 || len(config.StaticClients[0].RedirectURIs) < 1 { + return nil, fmt.Errorf("invalid dex config") + } + return &apisv1.DexConfigResponse{ + Issuer: config.Issuer, + ClientID: config.StaticClients[0].ID, + ClientSecret: config.StaticClients[0].Secret, + RedirectURL: config.StaticClients[0].RedirectURIs[0], + }, nil +} + +func (d *dexHandlerImpl) login(ctx context.Context) (*apisv1.LoginResponse, error) { + var claims struct { + Email string `json:"email"` + Name string `json:"name"` + } + if err := d.idToken.Claims(&claims); err != nil { + return nil, err + } + + user := &model.User{Email: claims.Email} + users, err := d.ds.List(ctx, user, &datastore.ListOptions{}) + if err != nil { + return nil, err + } + if len(users) > 0 { + u := users[0].(*model.User) + if u.Name != claims.Name { + u.Name = claims.Name + if err := d.ds.Put(ctx, u); err != nil { + return nil, err + } + } + } else if err := d.ds.Add(ctx, &model.User{ + Email: claims.Email, + Name: claims.Name, + }); err != nil { + return nil, err + } + + return &apisv1.LoginResponse{ + UserInfo: apisv1.DetailUserResponse{ + Name: claims.Name, + Email: claims.Email, + }, + AccessToken: d.token.AccessToken, + RefreshToken: d.token.RefreshToken, + }, nil +} diff --git a/pkg/apiserver/rest/usecase/authentication_test.go b/pkg/apiserver/rest/usecase/authentication_test.go new file mode 100644 index 000000000..9f0815e62 --- /dev/null +++ b/pkg/apiserver/rest/usecase/authentication_test.go @@ -0,0 +1,107 @@ +/* +Copyright 2022 The KubeVela 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 usecase + +import ( + "context" + "encoding/json" + "reflect" + "strconv" + "time" + + . "github.com/agiledragon/gomonkey/v2" + "github.com/coreos/go-oidc" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "golang.org/x/oauth2" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/oam-dev/kubevela/pkg/apiserver/datastore" + "github.com/oam-dev/kubevela/pkg/apiserver/model" + "github.com/oam-dev/kubevela/pkg/oam/util" +) + +var _ = Describe("Test authentication usecase functions", func() { + var ( + authUsecase *authenticationUsecaseImpl + ds datastore.DataStore + ) + + BeforeEach(func() { + var err error + ds, err = NewDatastore(datastore.Config{Type: "kubeapi", Database: "auth-test-" + strconv.FormatInt(time.Now().UnixNano(), 10)}) + Expect(ds).ToNot(BeNil()) + Expect(err).Should(BeNil()) + authUsecase = &authenticationUsecaseImpl{kubeClient: k8sClient, ds: ds} + }) + It("Test Dex login", func() { + testIDToken := &oidc.IDToken{} + patch := ApplyMethod(reflect.TypeOf(testIDToken), "Claims", func(_ *oidc.IDToken, v interface{}) error { + return json.Unmarshal([]byte(`{"email":"test@test.com","name":"test"}`), v) + }) + defer patch.Reset() + dexHandler := dexHandlerImpl{ + token: &oauth2.Token{ + AccessToken: "access-token", + RefreshToken: "refresh-token", + }, + idToken: testIDToken, + ds: ds, + } + resp, err := dexHandler.login(context.Background()) + Expect(err).Should(BeNil()) + Expect(resp.UserInfo.Email).Should(Equal("test@test.com")) + Expect(resp.UserInfo.Name).Should(Equal("test")) + Expect(resp.AccessToken).Should(Equal("access-token")) + Expect(resp.RefreshToken).Should(Equal("refresh-token")) + + user := &model.User{ + Name: "test", + } + err = ds.Get(context.Background(), user) + Expect(err).Should(BeNil()) + Expect(user.Email).Should(Equal("test@test.com")) + }) + + It("Test get dex config", func() { + err := k8sClient.Create(context.Background(), &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "vela-system", + }, + }) + Expect(err).Should(SatisfyAny(BeNil(), &util.AlreadyExistMatcher{})) + err = k8sClient.Create(context.Background(), &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: secretDexConfig, + Namespace: "vela-system", + }, + StringData: map[string]string{ + secretDexConfig: `{"issuer":"https://dex.oam.dev","staticClients":[{"id":"client-id","secret":"client-secret","redirectURIs":["http://localhost:8080/auth/callback"]}]}`, + }, + }) + Expect(err).Should(BeNil()) + + config, err := authUsecase.GetDexConfig(context.Background()) + Expect(err).Should(BeNil()) + Expect(config.Issuer).Should(Equal("https://dex.oam.dev")) + Expect(config.ClientID).Should(Equal("client-id")) + Expect(config.ClientSecret).Should(Equal("client-secret")) + Expect(config.RedirectURL).Should(Equal("http://localhost:8080/auth/callback")) + }) + +}) diff --git a/pkg/apiserver/rest/usecase/system_info.go b/pkg/apiserver/rest/usecase/system_info.go index aabc6f8ad..b657919f3 100644 --- a/pkg/apiserver/rest/usecase/system_info.go +++ b/pkg/apiserver/rest/usecase/system_info.go @@ -60,6 +60,7 @@ func (u systemInfoUsecaseImpl) GetSystemInfo(ctx context.Context) (*v1.SystemInf installID := rand.String(16) info.InstallID = installID info.EnableCollection = true + info.LoginType = model.LoginTypeLocal err = u.ds.Add(ctx, info) if err != nil { return nil, err diff --git a/pkg/apiserver/rest/utils/bcode/012_authentication.go b/pkg/apiserver/rest/utils/bcode/012_authentication.go new file mode 100644 index 000000000..4a200433b --- /dev/null +++ b/pkg/apiserver/rest/utils/bcode/012_authentication.go @@ -0,0 +1,20 @@ +/* +Copyright 2021 The KubeVela 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 bcode + +// ErrUnsupportedLoginType is the error of unsupported login type +var ErrUnsupportedLoginType = NewBcode(400, 12001, "the login type is not supported") diff --git a/pkg/apiserver/rest/webservice/authentication.go b/pkg/apiserver/rest/webservice/authentication.go new file mode 100644 index 000000000..1841f8006 --- /dev/null +++ b/pkg/apiserver/rest/webservice/authentication.go @@ -0,0 +1,86 @@ +/* +Copyright 2022 The KubeVela 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 webservice + +import ( + restfulspec "github.com/emicklei/go-restful-openapi/v2" + "github.com/emicklei/go-restful/v3" + + apis "github.com/oam-dev/kubevela/pkg/apiserver/rest/apis/v1" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/usecase" + "github.com/oam-dev/kubevela/pkg/apiserver/rest/utils/bcode" +) + +type authenticationWebService struct { + authenticationUsecase usecase.AuthenticationUsecase +} + +// NewAuthenticationWebService is the webservice of authentication +func NewAuthenticationWebService(authenticationUsecase usecase.AuthenticationUsecase) WebService { + return &authenticationWebService{ + authenticationUsecase: authenticationUsecase, + } +} + +func (c *authenticationWebService) GetWebService() *restful.WebService { + ws := new(restful.WebService) + ws.Path(versionPrefix). + Consumes(restful.MIME_XML, restful.MIME_JSON). + Produces(restful.MIME_JSON, restful.MIME_XML). + Doc("api for authentication manage") + + tags := []string{"authentication"} + + ws.Route(ws.GET("/login").To(c.login). + Doc("handle login request"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Returns(200, "", apis.LoginResponse{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.LoginResponse{})) + + ws.Route(ws.GET("/dexConfig").To(c.getDexConfig). + Doc("get Dex config"). + Metadata(restfulspec.KeyOpenAPITags, tags). + Returns(200, "", apis.DexConfigResponse{}). + Returns(400, "", bcode.Bcode{}). + Writes(apis.DexConfigResponse{})) + return ws +} + +func (c *authenticationWebService) login(req *restful.Request, res *restful.Response) { + base, err := c.authenticationUsecase.Login(req.Request.Context(), req) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := res.WriteEntity(base); err != nil { + bcode.ReturnError(req, res, err) + return + } +} + +func (c *authenticationWebService) getDexConfig(req *restful.Request, res *restful.Response) { + base, err := c.authenticationUsecase.GetDexConfig(req.Request.Context()) + if err != nil { + bcode.ReturnError(req, res, err) + return + } + if err := res.WriteEntity(base); err != nil { + bcode.ReturnError(req, res, err) + return + } +} diff --git a/pkg/apiserver/rest/webservice/webservice.go b/pkg/apiserver/rest/webservice/webservice.go index f5feac8b8..ba74b1c05 100644 --- a/pkg/apiserver/rest/webservice/webservice.go +++ b/pkg/apiserver/rest/webservice/webservice.go @@ -74,6 +74,7 @@ func Init(ds datastore.DataStore, addonCacheTime time.Duration) { webhookUsecase := usecase.NewWebhookUsecase(ds, applicationUsecase) systemInfoUsecase := usecase.NewSystemInfoUsecase(ds) helmUsecase := usecase.NewHelmUsecase() + authenticationUsecase := usecase.NewAuthenticationUsecase(ds, systemInfoUsecase) // init for default values @@ -97,6 +98,9 @@ func Init(ds datastore.DataStore, addonCacheTime time.Duration) { RegisterWebService(NewVelaQLWebService(velaQLUsecase)) RegisterWebService(NewWebhookWebService(webhookUsecase, applicationUsecase)) + // Authentication + RegisterWebService(NewAuthenticationWebService(authenticationUsecase)) + RegisterWebService(NewSystemInfoWebService(systemInfoUsecase)) RegisterWebService(NewHelmWebService(helmUsecase))