mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-20 01:05:19 +01:00
add security context based on admiral
This commit is contained in:
parent
6e89f11ffc
commit
8191f4a476
@ -107,6 +107,10 @@ func GetRepositoryByProjectName(name string) ([]*models.RepoRecord, error) {
|
|||||||
// in projectIDs
|
// in projectIDs
|
||||||
func GetTopRepos(projectIDs []int64, n int) ([]*models.RepoRecord, error) {
|
func GetTopRepos(projectIDs []int64, n int) ([]*models.RepoRecord, error) {
|
||||||
repositories := []*models.RepoRecord{}
|
repositories := []*models.RepoRecord{}
|
||||||
|
if len(projectIDs) == 0 {
|
||||||
|
return repositories, nil
|
||||||
|
}
|
||||||
|
|
||||||
_, err := GetOrmer().QueryTable(&models.RepoRecord{}).
|
_, err := GetOrmer().QueryTable(&models.RepoRecord{}).
|
||||||
Filter("project_id__in", projectIDs).
|
Filter("project_id__in", projectIDs).
|
||||||
OrderBy("-pull_count").
|
OrderBy("-pull_count").
|
||||||
|
174
src/common/security/admiral/context.go
Normal file
174
src/common/security/admiral/context.go
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 admiral
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/vmware/harbor/src/common"
|
||||||
|
"github.com/vmware/harbor/src/common/security/authcontext"
|
||||||
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
|
"github.com/vmware/harbor/src/ui/projectmanager"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SecurityContext implements security.Context interface based on
|
||||||
|
// auth context and project manager
|
||||||
|
type SecurityContext struct {
|
||||||
|
ctx *authcontext.AuthContext
|
||||||
|
pm projectmanager.ProjectManager
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewSecurityContext ...
|
||||||
|
func NewSecurityContext(ctx *authcontext.AuthContext, pm projectmanager.ProjectManager) *SecurityContext {
|
||||||
|
return &SecurityContext{
|
||||||
|
ctx: ctx,
|
||||||
|
pm: pm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsAuthenticated returns true if the user has been authenticated
|
||||||
|
func (s *SecurityContext) IsAuthenticated() bool {
|
||||||
|
if s.ctx == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return len(s.ctx.GetUsername()) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsername returns the username of the authenticated user
|
||||||
|
// It returns null if the user has not been authenticated
|
||||||
|
func (s *SecurityContext) GetUsername() string {
|
||||||
|
if !s.IsAuthenticated() {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return s.ctx.GetUsername()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSysAdmin returns whether the authenticated user is system admin
|
||||||
|
// It returns false if the user has not been authenticated
|
||||||
|
func (s *SecurityContext) IsSysAdmin() bool {
|
||||||
|
if !s.IsAuthenticated() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return s.ctx.IsSysAdmin()
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasReadPerm returns whether the user has read permission to the project
|
||||||
|
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
|
||||||
|
// public project
|
||||||
|
public, err := s.pm.IsPublic(projectIDOrName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to check the public of project %v: %v",
|
||||||
|
projectIDOrName, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if public {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// private project
|
||||||
|
if !s.IsAuthenticated() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// system admin
|
||||||
|
if s.IsSysAdmin() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, ok := projectIDOrName.(string); ok {
|
||||||
|
return s.ctx.HasReadPerm(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get roles of user %s to project %v: %v",
|
||||||
|
s.GetUsername(), projectIDOrName, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
switch role {
|
||||||
|
case common.RoleProjectAdmin,
|
||||||
|
common.RoleDeveloper,
|
||||||
|
common.RoleGuest:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasWritePerm returns whether the user has write permission to the project
|
||||||
|
func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
|
||||||
|
if !s.IsAuthenticated() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// system admin
|
||||||
|
if s.IsSysAdmin() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, ok := projectIDOrName.(string); ok {
|
||||||
|
return s.ctx.HasWritePerm(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get roles of user %s to project %v: %v",
|
||||||
|
s.GetUsername(), projectIDOrName, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
switch role {
|
||||||
|
case common.RoleProjectAdmin,
|
||||||
|
common.RoleDeveloper:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAllPerm returns whether the user has all permissions to the project
|
||||||
|
func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
|
||||||
|
if !s.IsAuthenticated() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// system admin
|
||||||
|
if s.IsSysAdmin() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if name, ok := projectIDOrName.(string); ok {
|
||||||
|
return s.ctx.HasAllPerm(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
roles, err := s.pm.GetRoles(s.GetUsername(), projectIDOrName)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get roles of user %s to project %v: %v",
|
||||||
|
s.GetUsername(), projectIDOrName, err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
switch role {
|
||||||
|
case common.RoleProjectAdmin:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
17
src/common/security/admiral/context_test.go
Normal file
17
src/common/security/admiral/context_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 admiral
|
||||||
|
|
||||||
|
// TODO add test cases
|
129
src/common/security/authcontext/authcontext.go
Normal file
129
src/common/security/authcontext/authcontext.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 authcontext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/ui/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// AuthTokenHeader is the key of auth token header
|
||||||
|
AuthTokenHeader = "x-xenon-auth-token"
|
||||||
|
sysAdminRole = "CLOUD_ADMIN"
|
||||||
|
projectAdminRole = "DEVOPS_ADMIN"
|
||||||
|
developerRole = "DEVELOPER"
|
||||||
|
guestRole = "GUEST"
|
||||||
|
)
|
||||||
|
|
||||||
|
var client = &http.Client{
|
||||||
|
Transport: &http.Transport{},
|
||||||
|
}
|
||||||
|
|
||||||
|
// AuthContext ...
|
||||||
|
type AuthContext struct {
|
||||||
|
PrincipalID string `json:"principalId"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Roles []string `json:"projects"`
|
||||||
|
Projects map[string][]string `json:"roles"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetUsername ...
|
||||||
|
func (a *AuthContext) GetUsername() string {
|
||||||
|
return a.PrincipalID
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSysAdmin ...
|
||||||
|
func (a *AuthContext) IsSysAdmin() bool {
|
||||||
|
isSysAdmin := false
|
||||||
|
for _, role := range a.Roles {
|
||||||
|
// TODO update the value of role when admiral API is ready
|
||||||
|
if role == sysAdminRole {
|
||||||
|
isSysAdmin = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isSysAdmin
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasReadPerm ...
|
||||||
|
func (a *AuthContext) HasReadPerm(project string) bool {
|
||||||
|
_, exist := a.Projects[project]
|
||||||
|
return exist
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasWritePerm ...
|
||||||
|
func (a *AuthContext) HasWritePerm(project string) bool {
|
||||||
|
roles, _ := a.Projects[project]
|
||||||
|
for _, role := range roles {
|
||||||
|
if role == projectAdminRole || role == developerRole {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasAllPerm ...
|
||||||
|
func (a *AuthContext) HasAllPerm(project string) bool {
|
||||||
|
roles, _ := a.Projects[project]
|
||||||
|
for _, role := range roles {
|
||||||
|
if role == projectAdminRole {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetByToken ...
|
||||||
|
func GetByToken(token string) (*AuthContext, error) {
|
||||||
|
endpoint := config.AdmiralEndpoint()
|
||||||
|
path := strings.TrimRight(endpoint, "/") + "/sso/auth-context"
|
||||||
|
req, err := http.NewRequest(http.MethodGet, path, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Header.Add(AuthTokenHeader, token)
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("failed to get auth context by token: %d %s",
|
||||||
|
resp.StatusCode, string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := &AuthContext{
|
||||||
|
Projects: make(map[string][]string),
|
||||||
|
}
|
||||||
|
if err = json.Unmarshal(data, ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, nil
|
||||||
|
}
|
17
src/common/security/authcontext/authcontext_test.go
Normal file
17
src/common/security/authcontext/authcontext_test.go
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright (c) 2017 VMware, Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// 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 authcontext
|
||||||
|
|
||||||
|
// TODO add test cases
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package rbac
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/vmware/harbor/src/common"
|
"github.com/vmware/harbor/src/common"
|
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package rbac
|
package local
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
@ -39,14 +39,14 @@ type BaseController struct {
|
|||||||
func (b *BaseController) Prepare() {
|
func (b *BaseController) Prepare() {
|
||||||
ctx, err := filter.GetSecurityContext(b.Ctx.Request)
|
ctx, err := filter.GetSecurityContext(b.Ctx.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to get security context")
|
log.Errorf("failed to get security context: %v", err)
|
||||||
b.CustomAbort(http.StatusInternalServerError, "")
|
b.CustomAbort(http.StatusInternalServerError, "")
|
||||||
}
|
}
|
||||||
b.SecurityCtx = ctx
|
b.SecurityCtx = ctx
|
||||||
|
|
||||||
pm, err := filter.GetProjectManager(b.Ctx.Request)
|
pm, err := filter.GetProjectManager(b.Ctx.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("failed to get project manager")
|
log.Errorf("failed to get project manager: %v", err)
|
||||||
b.CustomAbort(http.StatusInternalServerError, "")
|
b.CustomAbort(http.StatusInternalServerError, "")
|
||||||
}
|
}
|
||||||
b.ProjectMgr = pm
|
b.ProjectMgr = pm
|
||||||
|
@ -87,6 +87,7 @@ func init() {
|
|||||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||||
beego.TestBeegoInit(apppath)
|
beego.TestBeegoInit(apppath)
|
||||||
|
|
||||||
|
filter.Init()
|
||||||
beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter)
|
beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter)
|
||||||
|
|
||||||
beego.Router("/api/search/", &SearchAPI{})
|
beego.Router("/api/search/", &SearchAPI{})
|
||||||
|
@ -28,6 +28,7 @@ import (
|
|||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
"github.com/vmware/harbor/src/ui/projectmanager"
|
"github.com/vmware/harbor/src/ui/projectmanager"
|
||||||
"github.com/vmware/harbor/src/ui/projectmanager/db"
|
"github.com/vmware/harbor/src/ui/projectmanager/db"
|
||||||
|
"github.com/vmware/harbor/src/ui/projectmanager/pms"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -96,10 +97,16 @@ func initSecretStore() {
|
|||||||
|
|
||||||
func initProjectManager() {
|
func initProjectManager() {
|
||||||
if !WithAdmiral() {
|
if !WithAdmiral() {
|
||||||
|
// standalone
|
||||||
log.Info("initializing the project manager based on database...")
|
log.Info("initializing the project manager based on database...")
|
||||||
GlobalProjectMgr = &db.ProjectManager{}
|
GlobalProjectMgr = &db.ProjectManager{}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// TODO create project manager based on pms
|
|
||||||
|
// integration with admiral
|
||||||
|
// TODO create project manager based on pms using service account
|
||||||
|
log.Info("initializing the project manager based on PMS...")
|
||||||
|
GlobalProjectMgr = pms.NewProjectManager(AdmiralEndpoint(), "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load configurations
|
// Load configurations
|
||||||
|
@ -22,8 +22,11 @@ import (
|
|||||||
|
|
||||||
beegoctx "github.com/astaxie/beego/context"
|
beegoctx "github.com/astaxie/beego/context"
|
||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
|
secstore "github.com/vmware/harbor/src/common/secret"
|
||||||
"github.com/vmware/harbor/src/common/security"
|
"github.com/vmware/harbor/src/common/security"
|
||||||
"github.com/vmware/harbor/src/common/security/rbac"
|
"github.com/vmware/harbor/src/common/security/admiral"
|
||||||
|
"github.com/vmware/harbor/src/common/security/authcontext"
|
||||||
|
"github.com/vmware/harbor/src/common/security/local"
|
||||||
"github.com/vmware/harbor/src/common/security/secret"
|
"github.com/vmware/harbor/src/common/security/secret"
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
"github.com/vmware/harbor/src/ui/auth"
|
"github.com/vmware/harbor/src/ui/auth"
|
||||||
@ -35,12 +38,33 @@ import (
|
|||||||
type key string
|
type key string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// HarborSecurityContext is the name of security context passed to handlers
|
securCtxKey key = "harbor_security_context"
|
||||||
HarborSecurityContext key = "harbor_security_context"
|
pmKey key = "harbor_project_manager"
|
||||||
// HarborProjectManager is the name of project manager passed to handlers
|
|
||||||
HarborProjectManager key = "harbor_project_manager"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
reqCtxModifiers []ReqCtxModifier
|
||||||
|
)
|
||||||
|
|
||||||
|
// Init ReqCtxMofiers list
|
||||||
|
func Init() {
|
||||||
|
// integration with admiral
|
||||||
|
if config.WithAdmiral() {
|
||||||
|
reqCtxModifiers = []ReqCtxModifier{
|
||||||
|
&secretReqCtxModifier{},
|
||||||
|
&tokenReqCtxModifier{},
|
||||||
|
&unauthorizedReqCtxModifier{}}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// standalone
|
||||||
|
reqCtxModifiers = []ReqCtxModifier{
|
||||||
|
&secretReqCtxModifier{},
|
||||||
|
&basicAuthReqCtxModifier{},
|
||||||
|
&sessionReqCtxModifier{},
|
||||||
|
&unauthorizedReqCtxModifier{}}
|
||||||
|
}
|
||||||
|
|
||||||
// SecurityFilter authenticates the request and passes a security context
|
// SecurityFilter authenticates the request and passes a security context
|
||||||
// and a project manager with it which can be used to do some authN & authZ
|
// and a project manager with it which can be used to do some authN & authZ
|
||||||
func SecurityFilter(ctx *beegoctx.Context) {
|
func SecurityFilter(ctx *beegoctx.Context) {
|
||||||
@ -54,93 +78,175 @@ func SecurityFilter(ctx *beegoctx.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !strings.HasPrefix(req.URL.RequestURI(), "/api/") &&
|
if !strings.HasPrefix(req.URL.RequestURI(), "/api/") &&
|
||||||
!strings.HasPrefix(req.URL.RequestURI(), "/service/") {
|
!strings.HasPrefix(req.URL.RequestURI(), "/service/token") {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill ctx with security context and project manager
|
// add security context and project manager to request context
|
||||||
fillContext(ctx)
|
for _, modifier := range reqCtxModifiers {
|
||||||
|
if modifier.Modify(ctx) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fillContext(ctx *beegoctx.Context) {
|
// ReqCtxModifier modifies the context of request
|
||||||
// secret
|
type ReqCtxModifier interface {
|
||||||
|
Modify(*beegoctx.Context) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type secretReqCtxModifier struct {
|
||||||
|
store *secstore.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *secretReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||||
scrt := ctx.GetCookie("secret")
|
scrt := ctx.GetCookie("secret")
|
||||||
if len(scrt) != 0 {
|
if len(scrt) == 0 {
|
||||||
ct := context.WithValue(ctx.Request.Context(),
|
return false
|
||||||
HarborProjectManager,
|
|
||||||
getProjectManager(ctx))
|
|
||||||
|
|
||||||
log.Info("creating a secret security context...")
|
|
||||||
ct = context.WithValue(ct, HarborSecurityContext,
|
|
||||||
secret.NewSecurityContext(scrt, config.SecretStore))
|
|
||||||
|
|
||||||
ctx.Request = ctx.Request.WithContext(ct)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var user *models.User
|
log.Debug("got secret from request")
|
||||||
var err error
|
|
||||||
|
|
||||||
// basic auth
|
var pm projectmanager.ProjectManager
|
||||||
|
if config.WithAdmiral() {
|
||||||
|
// TODO project manager with harbor service accout
|
||||||
|
} else {
|
||||||
|
log.Debug("using local database project manager")
|
||||||
|
pm = config.GlobalProjectMgr
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debug("creating a secret security context...")
|
||||||
|
securCtx := secret.NewSecurityContext(scrt, s.store)
|
||||||
|
setSecurCtxAndPM(ctx.Request, securCtx, pm)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type basicAuthReqCtxModifier struct{}
|
||||||
|
|
||||||
|
func (b *basicAuthReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||||
username, password, ok := ctx.Request.BasicAuth()
|
username, password, ok := ctx.Request.BasicAuth()
|
||||||
if ok {
|
if !ok {
|
||||||
// TODO the return data contains other params when integrated
|
return false
|
||||||
// with vic
|
}
|
||||||
user, err = auth.Login(models.AuthModel{
|
|
||||||
|
user, err := auth.Login(models.AuthModel{
|
||||||
Principal: username,
|
Principal: username,
|
||||||
Password: password,
|
Password: password,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to authenticate %s: %v", username, err)
|
log.Errorf("failed to authenticate %s: %v", username, err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
if user != nil {
|
if user == nil {
|
||||||
log.Info("got user information via basic auth")
|
return false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// session
|
var securCtx security.Context
|
||||||
if user == nil {
|
var pm projectmanager.ProjectManager
|
||||||
|
log.Debug("got user information via basic auth")
|
||||||
|
if config.WithAdmiral() {
|
||||||
|
// integration with admiral
|
||||||
|
// we can add logic here to support basic auth in integration mode
|
||||||
|
log.Debug("basic auth isn't supported in integration mode")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// standalone
|
||||||
|
log.Debug("using local database project manager")
|
||||||
|
pm = config.GlobalProjectMgr
|
||||||
|
log.Debug("creating local database security context...")
|
||||||
|
securCtx = local.NewSecurityContext(user, pm)
|
||||||
|
|
||||||
|
setSecurCtxAndPM(ctx.Request, securCtx, pm)
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
type sessionReqCtxModifier struct{}
|
||||||
|
|
||||||
|
func (s *sessionReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||||
username := ctx.Input.Session("username")
|
username := ctx.Input.Session("username")
|
||||||
isSysAdmin := ctx.Input.Session("isSysAdmin")
|
if username == nil {
|
||||||
if username != nil {
|
return false
|
||||||
user = &models.User{
|
}
|
||||||
|
|
||||||
|
log.Debug("got user information from session")
|
||||||
|
user := &models.User{
|
||||||
Username: username.(string),
|
Username: username.(string),
|
||||||
}
|
}
|
||||||
|
isSysAdmin := ctx.Input.Session("isSysAdmin")
|
||||||
if isSysAdmin != nil && isSysAdmin.(bool) {
|
if isSysAdmin != nil && isSysAdmin.(bool) {
|
||||||
user.HasAdminRole = 1
|
user.HasAdminRole = 1
|
||||||
}
|
}
|
||||||
log.Info("got user information from session")
|
|
||||||
|
log.Debug("using local database project manager")
|
||||||
|
pm := config.GlobalProjectMgr
|
||||||
|
log.Debug("creating local database security context...")
|
||||||
|
securCtx := local.NewSecurityContext(user, pm)
|
||||||
|
|
||||||
|
setSecurCtxAndPM(ctx.Request, securCtx, pm)
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO maybe need to get token from session
|
type tokenReqCtxModifier struct{}
|
||||||
|
|
||||||
|
func (t *tokenReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||||
|
token := ctx.Request.Header.Get(authcontext.AuthTokenHeader)
|
||||||
|
if len(token) == 0 {
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if user == nil {
|
log.Debug("got token from request")
|
||||||
log.Info("user information is nil")
|
|
||||||
|
authContext, err := authcontext.GetByToken(token)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get auth context: %v", err)
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
pm := getProjectManager(ctx)
|
log.Debug("creating PMS project manager...")
|
||||||
ct := context.WithValue(ctx.Request.Context(), HarborProjectManager, pm)
|
pm := pms.NewProjectManager(config.AdmiralEndpoint(), token)
|
||||||
|
log.Debug("creating admiral security context...")
|
||||||
|
securCtx := admiral.NewSecurityContext(authContext, pm)
|
||||||
|
setSecurCtxAndPM(ctx.Request, securCtx, pm)
|
||||||
|
|
||||||
log.Info("creating a rbac security context...")
|
return true
|
||||||
ct = context.WithValue(ct, HarborSecurityContext,
|
|
||||||
rbac.NewSecurityContext(user, pm))
|
|
||||||
ctx.Request = ctx.Request.WithContext(ct)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getProjectManager(ctx *beegoctx.Context) projectmanager.ProjectManager {
|
// use this one as the last modifier in the modifier list for unauthorized request
|
||||||
if !config.WithAdmiral() {
|
type unauthorizedReqCtxModifier struct{}
|
||||||
log.Info("filling a project manager based on database...")
|
|
||||||
return config.GlobalProjectMgr
|
func (u *unauthorizedReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
||||||
|
log.Debug("user information is nil")
|
||||||
|
|
||||||
|
var securCtx security.Context
|
||||||
|
var pm projectmanager.ProjectManager
|
||||||
|
if config.WithAdmiral() {
|
||||||
|
// integration with admiral
|
||||||
|
log.Debug("creating PMS project manager...")
|
||||||
|
pm = pms.NewProjectManager(config.AdmiralEndpoint(), "")
|
||||||
|
log.Debug("creating admiral security context...")
|
||||||
|
securCtx = admiral.NewSecurityContext(nil, pm)
|
||||||
|
} else {
|
||||||
|
// standalone
|
||||||
|
log.Debug("using local database project manager")
|
||||||
|
pm = config.GlobalProjectMgr
|
||||||
|
log.Debug("creating local database security context...")
|
||||||
|
securCtx = local.NewSecurityContext(nil, pm)
|
||||||
|
}
|
||||||
|
setSecurCtxAndPM(ctx.Request, securCtx, pm)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Info("filling a project manager based on PMS...")
|
func setSecurCtxAndPM(req *http.Request, ctx security.Context, pm projectmanager.ProjectManager) {
|
||||||
// TODO pass the token to the function
|
addToReqContext(req, securCtxKey, ctx)
|
||||||
return pms.NewProjectManager(config.AdmiralEndpoint(), "")
|
addToReqContext(req, pmKey, pm)
|
||||||
|
}
|
||||||
|
|
||||||
|
func addToReqContext(req *http.Request, key, value interface{}) {
|
||||||
|
*req = *(req.WithContext(context.WithValue(req.Context(), key, value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSecurityContext tries to get security context from request and returns it
|
// GetSecurityContext tries to get security context from request and returns it
|
||||||
@ -149,7 +255,7 @@ func GetSecurityContext(req *http.Request) (security.Context, error) {
|
|||||||
return nil, fmt.Errorf("request is nil")
|
return nil, fmt.Errorf("request is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx := req.Context().Value(HarborSecurityContext)
|
ctx := req.Context().Value(securCtxKey)
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return nil, fmt.Errorf("the security context got from request is nil")
|
return nil, fmt.Errorf("the security context got from request is nil")
|
||||||
}
|
}
|
||||||
@ -168,7 +274,7 @@ func GetProjectManager(req *http.Request) (projectmanager.ProjectManager, error)
|
|||||||
return nil, fmt.Errorf("request is nil")
|
return nil, fmt.Errorf("request is nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
pm := req.Context().Value(HarborProjectManager)
|
pm := req.Context().Value(pmKey)
|
||||||
if pm == nil {
|
if pm == nil {
|
||||||
return nil, fmt.Errorf("the project manager got from request is nil")
|
return nil, fmt.Errorf("the project manager got from request is nil")
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
"github.com/vmware/harbor/src/common/security"
|
"github.com/vmware/harbor/src/common/security"
|
||||||
"github.com/vmware/harbor/src/common/security/rbac"
|
"github.com/vmware/harbor/src/common/security/local"
|
||||||
"github.com/vmware/harbor/src/common/security/secret"
|
"github.com/vmware/harbor/src/common/security/secret"
|
||||||
_ "github.com/vmware/harbor/src/ui/auth/db"
|
_ "github.com/vmware/harbor/src/ui/auth/db"
|
||||||
_ "github.com/vmware/harbor/src/ui/auth/ldap"
|
_ "github.com/vmware/harbor/src/ui/auth/ldap"
|
||||||
@ -73,6 +73,8 @@ func TestMain(m *testing.M) {
|
|||||||
log.Fatalf("failed to initialize database: %v", err)
|
log.Fatalf("failed to initialize database: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Init()
|
||||||
|
|
||||||
os.Exit(m.Run())
|
os.Exit(m.Run())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +82,7 @@ func TestSecurityFilter(t *testing.T) {
|
|||||||
// nil request
|
// nil request
|
||||||
ctx, err := newContext(nil)
|
ctx, err := newContext(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to crate context: %v", err)
|
t.Fatalf("failed to create context: %v", err)
|
||||||
}
|
}
|
||||||
SecurityFilter(ctx)
|
SecurityFilter(ctx)
|
||||||
assert.Nil(t, securityContext(ctx))
|
assert.Nil(t, securityContext(ctx))
|
||||||
@ -116,8 +118,7 @@ func TestSecurityFilter(t *testing.T) {
|
|||||||
assert.NotNil(t, projectManager(ctx))
|
assert.NotNil(t, projectManager(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestFillContext(t *testing.T) {
|
func TestSecretReqCtxModifier(t *testing.T) {
|
||||||
// secret
|
|
||||||
req, err := http.NewRequest(http.MethodGet,
|
req, err := http.NewRequest(http.MethodGet,
|
||||||
"http://127.0.0.1/api/projects/", nil)
|
"http://127.0.0.1/api/projects/", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -133,13 +134,40 @@ func TestFillContext(t *testing.T) {
|
|||||||
t.Fatalf("failed to crate context: %v", err)
|
t.Fatalf("failed to crate context: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fillContext(ctx)
|
modifier := &secretReqCtxModifier{}
|
||||||
|
modified := modifier.Modify(ctx)
|
||||||
|
assert.True(t, modified)
|
||||||
assert.IsType(t, &secret.SecurityContext{},
|
assert.IsType(t, &secret.SecurityContext{},
|
||||||
securityContext(ctx))
|
securityContext(ctx))
|
||||||
assert.NotNil(t, projectManager(ctx))
|
assert.NotNil(t, projectManager(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
// session
|
func TestBasicAuthReqCtxModifier(t *testing.T) {
|
||||||
req, err = http.NewRequest(http.MethodGet,
|
req, err := http.NewRequest(http.MethodGet,
|
||||||
|
"http://127.0.0.1/api/projects/", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to create request: %v", req)
|
||||||
|
}
|
||||||
|
req.SetBasicAuth("admin", "Harbor12345")
|
||||||
|
|
||||||
|
ctx, err := newContext(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to crate context: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier := &basicAuthReqCtxModifier{}
|
||||||
|
modified := modifier.Modify(ctx)
|
||||||
|
assert.True(t, modified)
|
||||||
|
|
||||||
|
sc := securityContext(ctx)
|
||||||
|
assert.IsType(t, &local.SecurityContext{}, sc)
|
||||||
|
s := sc.(security.Context)
|
||||||
|
assert.Equal(t, "admin", s.GetUsername())
|
||||||
|
assert.NotNil(t, projectManager(ctx))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSessionReqCtxModifier(t *testing.T) {
|
||||||
|
req, err := http.NewRequest(http.MethodGet,
|
||||||
"http://127.0.0.1/api/projects/", nil)
|
"http://127.0.0.1/api/projects/", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create request: %v", req)
|
t.Fatalf("failed to create request: %v", req)
|
||||||
@ -162,52 +190,47 @@ func TestFillContext(t *testing.T) {
|
|||||||
}
|
}
|
||||||
addSessionIDToCookie(req, store.SessionID())
|
addSessionIDToCookie(req, store.SessionID())
|
||||||
|
|
||||||
ctx, err = newContext(req)
|
ctx, err := newContext(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to crate context: %v", err)
|
t.Fatalf("failed to crate context: %v", err)
|
||||||
}
|
}
|
||||||
fillContext(ctx)
|
|
||||||
|
modifier := &sessionReqCtxModifier{}
|
||||||
|
modified := modifier.Modify(ctx)
|
||||||
|
|
||||||
|
assert.True(t, modified)
|
||||||
sc := securityContext(ctx)
|
sc := securityContext(ctx)
|
||||||
assert.IsType(t, &rbac.SecurityContext{}, sc)
|
assert.IsType(t, &local.SecurityContext{}, sc)
|
||||||
s := sc.(security.Context)
|
s := sc.(security.Context)
|
||||||
assert.Equal(t, "admin", s.GetUsername())
|
assert.Equal(t, "admin", s.GetUsername())
|
||||||
assert.True(t, s.IsSysAdmin())
|
assert.True(t, s.IsSysAdmin())
|
||||||
assert.NotNil(t, projectManager(ctx))
|
assert.NotNil(t, projectManager(ctx))
|
||||||
|
|
||||||
// basic auth
|
|
||||||
req, err = http.NewRequest(http.MethodGet,
|
|
||||||
"http://127.0.0.1/api/projects/", nil)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("failed to create request: %v", req)
|
|
||||||
}
|
}
|
||||||
req.SetBasicAuth("admin", "Harbor12345")
|
|
||||||
|
|
||||||
ctx, err = newContext(req)
|
// TODO add test case
|
||||||
if err != nil {
|
func TestTokenReqCtxModifier(t *testing.T) {
|
||||||
t.Fatalf("failed to crate context: %v", err)
|
|
||||||
}
|
}
|
||||||
fillContext(ctx)
|
|
||||||
sc = securityContext(ctx)
|
|
||||||
assert.IsType(t, &rbac.SecurityContext{}, sc)
|
|
||||||
s = sc.(security.Context)
|
|
||||||
assert.Equal(t, "admin", s.GetUsername())
|
|
||||||
assert.NotNil(t, projectManager(ctx))
|
|
||||||
|
|
||||||
// no credential
|
func TestUnauthorizedReqCtxModifier(t *testing.T) {
|
||||||
req, err = http.NewRequest(http.MethodGet,
|
req, err := http.NewRequest(http.MethodGet,
|
||||||
"http://127.0.0.1/api/projects/", nil)
|
"http://127.0.0.1/api/projects/", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to create request: %v", req)
|
t.Fatalf("failed to create request: %v", req)
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, err = newContext(req)
|
ctx, err := newContext(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to crate context: %v", err)
|
t.Fatalf("failed to crate context: %v", err)
|
||||||
}
|
}
|
||||||
fillContext(ctx)
|
|
||||||
sc = securityContext(ctx)
|
modifier := &unauthorizedReqCtxModifier{}
|
||||||
assert.IsType(t, &rbac.SecurityContext{}, sc)
|
modified := modifier.Modify(ctx)
|
||||||
s = sc.(security.Context)
|
assert.True(t, modified)
|
||||||
|
|
||||||
|
sc := securityContext(ctx)
|
||||||
|
assert.NotNil(t, sc)
|
||||||
|
s := sc.(security.Context)
|
||||||
assert.False(t, s.IsAuthenticated())
|
assert.False(t, s.IsAuthenticated())
|
||||||
assert.NotNil(t, projectManager(ctx))
|
assert.NotNil(t, projectManager(ctx))
|
||||||
}
|
}
|
||||||
@ -241,6 +264,7 @@ func addSessionIDToCookie(req *http.Request, sessionID string) {
|
|||||||
func securityContext(ctx *beegoctx.Context) interface{} {
|
func securityContext(ctx *beegoctx.Context) interface{} {
|
||||||
c, err := GetSecurityContext(ctx.Request)
|
c, err := GetSecurityContext(ctx.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
log.Printf("failed to get security context: %v \n", err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return c
|
return c
|
||||||
@ -250,7 +274,7 @@ func projectManager(ctx *beegoctx.Context) interface{} {
|
|||||||
if ctx.Request == nil {
|
if ctx.Request == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return ctx.Request.Context().Value(HarborProjectManager)
|
return ctx.Request.Context().Value(pmKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSecurityContext(t *testing.T) {
|
func TestGetSecurityContext(t *testing.T) {
|
||||||
@ -268,7 +292,7 @@ func TestGetSecurityContext(t *testing.T) {
|
|||||||
req, err = http.NewRequest("", "", nil)
|
req, err = http.NewRequest("", "", nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
req = req.WithContext(context.WithValue(req.Context(),
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
HarborSecurityContext, "test"))
|
securCtxKey, "test"))
|
||||||
ctx, err = GetSecurityContext(req)
|
ctx, err = GetSecurityContext(req)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
@ -276,7 +300,7 @@ func TestGetSecurityContext(t *testing.T) {
|
|||||||
req, err = http.NewRequest("", "", nil)
|
req, err = http.NewRequest("", "", nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
req = req.WithContext(context.WithValue(req.Context(),
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
HarborSecurityContext, rbac.NewSecurityContext(nil, nil)))
|
securCtxKey, local.NewSecurityContext(nil, nil)))
|
||||||
ctx, err = GetSecurityContext(req)
|
ctx, err = GetSecurityContext(req)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, ok := ctx.(security.Context)
|
_, ok := ctx.(security.Context)
|
||||||
@ -298,7 +322,7 @@ func TestGetProjectManager(t *testing.T) {
|
|||||||
req, err = http.NewRequest("", "", nil)
|
req, err = http.NewRequest("", "", nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
req = req.WithContext(context.WithValue(req.Context(),
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
HarborProjectManager, "test"))
|
pmKey, "test"))
|
||||||
pm, err = GetProjectManager(req)
|
pm, err = GetProjectManager(req)
|
||||||
assert.NotNil(t, err)
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
@ -306,7 +330,7 @@ func TestGetProjectManager(t *testing.T) {
|
|||||||
req, err = http.NewRequest("", "", nil)
|
req, err = http.NewRequest("", "", nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
req = req.WithContext(context.WithValue(req.Context(),
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
HarborProjectManager, &db.ProjectManager{}))
|
pmKey, &db.ProjectManager{}))
|
||||||
pm, err = GetProjectManager(req)
|
pm, err = GetProjectManager(req)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
_, ok := pm.(projectmanager.ProjectManager)
|
_, ok := pm.(projectmanager.ProjectManager)
|
||||||
|
@ -98,6 +98,7 @@ func main() {
|
|||||||
log.Error(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filter.Init()
|
||||||
beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter)
|
beego.InsertFilter("/*", beego.BeforeRouter, filter.SecurityFilter)
|
||||||
|
|
||||||
initRouters()
|
initRouters()
|
||||||
|
Loading…
Reference in New Issue
Block a user