mirror of
https://github.com/goharbor/harbor.git
synced 2025-02-18 12:51:27 +01:00
Merge pull request #2290 from ywk253100/170511_merge
Refactor auth of token service
This commit is contained in:
commit
e28ad39430
@ -136,6 +136,7 @@ func (b *BaseAPI) ValidateUser() int {
|
|||||||
|
|
||||||
// GetUserIDForRequest tries to get user ID from basic auth header and session.
|
// GetUserIDForRequest tries to get user ID from basic auth header and session.
|
||||||
// It returns the user ID, whether need further verification(when the id is from session) and if the action is successful
|
// It returns the user ID, whether need further verification(when the id is from session) and if the action is successful
|
||||||
|
// TODO remove
|
||||||
func (b *BaseAPI) GetUserIDForRequest() (int, bool, bool) {
|
func (b *BaseAPI) GetUserIDForRequest() (int, bool, bool) {
|
||||||
username, password, ok := b.Ctx.Request.BasicAuth()
|
username, password, ok := b.Ctx.Request.BasicAuth()
|
||||||
if ok {
|
if ok {
|
||||||
|
@ -59,6 +59,11 @@ func (s *SecurityContext) IsSysAdmin() bool {
|
|||||||
|
|
||||||
// HasReadPerm returns whether the user has read permission to the project
|
// HasReadPerm returns whether the user has read permission to the project
|
||||||
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
|
func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
|
||||||
|
// not exist
|
||||||
|
if !s.pm.Exist(projectIDOrName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// public project
|
// public project
|
||||||
if s.pm.IsPublic(projectIDOrName) {
|
if s.pm.IsPublic(projectIDOrName) {
|
||||||
return true
|
return true
|
||||||
@ -93,6 +98,11 @@ func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// project does not exist
|
||||||
|
if !s.pm.Exist(projectIDOrName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// system admin
|
// system admin
|
||||||
if s.IsSysAdmin() {
|
if s.IsSysAdmin() {
|
||||||
return true
|
return true
|
||||||
@ -115,6 +125,12 @@ func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
|
|||||||
if !s.IsAuthenticated() {
|
if !s.IsAuthenticated() {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// project does not exist
|
||||||
|
if !s.pm.Exist(projectIDOrName) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// system admin
|
// system admin
|
||||||
if s.IsSysAdmin() {
|
if s.IsSysAdmin() {
|
||||||
return true
|
return true
|
||||||
|
@ -22,26 +22,69 @@ import (
|
|||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
public = &models.Project{
|
||||||
|
Name: "public_project",
|
||||||
|
Public: 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
private = &models.Project{
|
||||||
|
Name: "private_project",
|
||||||
|
Public: 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
read = &models.Project{
|
||||||
|
Name: "has_read_perm_project",
|
||||||
|
}
|
||||||
|
|
||||||
|
write = &models.Project{
|
||||||
|
Name: "has_write_perm_project",
|
||||||
|
}
|
||||||
|
|
||||||
|
all = &models.Project{
|
||||||
|
Name: "has_all_perm_project",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
type fakePM struct {
|
type fakePM struct {
|
||||||
public string
|
projects []*models.Project
|
||||||
roles map[string][]int
|
roles map[string][]int
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *fakePM) IsPublic(projectIDOrName interface{}) bool {
|
func (f *fakePM) IsPublic(projectIDOrName interface{}) bool {
|
||||||
return f.public == projectIDOrName.(string)
|
for _, project := range f.projects {
|
||||||
|
if project.Name == projectIDOrName.(string) {
|
||||||
|
return project.Public == 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
func (f *fakePM) GetRoles(username string, projectIDOrName interface{}) []int {
|
func (f *fakePM) GetRoles(username string, projectIDOrName interface{}) []int {
|
||||||
return f.roles[projectIDOrName.(string)]
|
return f.roles[projectIDOrName.(string)]
|
||||||
}
|
}
|
||||||
func (f *fakePM) Get(projectIDOrName interface{}) *models.Project {
|
func (f *fakePM) Get(projectIDOrName interface{}) *models.Project {
|
||||||
|
for _, project := range f.projects {
|
||||||
|
if project.Name == projectIDOrName.(string) {
|
||||||
|
return project
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (f *fakePM) Exist(projectIDOrName interface{}) bool {
|
func (f *fakePM) Exist(projectIDOrName interface{}) bool {
|
||||||
|
for _, project := range f.projects {
|
||||||
|
if project.Name == projectIDOrName.(string) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nil implement
|
||||||
func (f *fakePM) GetPublic() []models.Project {
|
func (f *fakePM) GetPublic() []models.Project {
|
||||||
return []models.Project{}
|
return []models.Project{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nil implement
|
||||||
func (f *fakePM) GetByMember(username string) []models.Project {
|
func (f *fakePM) GetByMember(username string) []models.Project {
|
||||||
return []models.Project{}
|
return []models.Project{}
|
||||||
}
|
}
|
||||||
@ -91,25 +134,29 @@ func TestIsSysAdmin(t *testing.T) {
|
|||||||
|
|
||||||
func TestHasReadPerm(t *testing.T) {
|
func TestHasReadPerm(t *testing.T) {
|
||||||
pm := &fakePM{
|
pm := &fakePM{
|
||||||
public: "public_project",
|
projects: []*models.Project{public, private, read},
|
||||||
roles: map[string][]int{
|
roles: map[string][]int{
|
||||||
"has_read_perm_project": []int{common.RoleGuest},
|
"has_read_perm_project": []int{common.RoleGuest},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
// public project, unauthenticated
|
// non-exist project
|
||||||
ctx := NewSecurityContext(nil, pm)
|
ctx := NewSecurityContext(nil, pm)
|
||||||
|
assert.False(t, ctx.HasReadPerm("non_exist_project"))
|
||||||
|
|
||||||
|
// public project
|
||||||
|
ctx = NewSecurityContext(nil, pm)
|
||||||
assert.True(t, ctx.HasReadPerm("public_project"))
|
assert.True(t, ctx.HasReadPerm("public_project"))
|
||||||
|
|
||||||
// private project, unauthenticated
|
// private project, unauthenticated
|
||||||
ctx = NewSecurityContext(nil, pm)
|
ctx = NewSecurityContext(nil, pm)
|
||||||
assert.False(t, ctx.HasReadPerm("has_read_perm_project"))
|
assert.False(t, ctx.HasReadPerm("private_project"))
|
||||||
|
|
||||||
// private project, authenticated, has no perm
|
// private project, authenticated, has no perm
|
||||||
ctx = NewSecurityContext(&models.User{
|
ctx = NewSecurityContext(&models.User{
|
||||||
Username: "test",
|
Username: "test",
|
||||||
}, pm)
|
}, pm)
|
||||||
assert.False(t, ctx.HasReadPerm("has_no_perm_project"))
|
assert.False(t, ctx.HasReadPerm("private_project"))
|
||||||
|
|
||||||
// private project, authenticated, has read perm
|
// private project, authenticated, has read perm
|
||||||
ctx = NewSecurityContext(&models.User{
|
ctx = NewSecurityContext(&models.User{
|
||||||
@ -122,11 +169,19 @@ func TestHasReadPerm(t *testing.T) {
|
|||||||
Username: "test",
|
Username: "test",
|
||||||
HasAdminRole: 1,
|
HasAdminRole: 1,
|
||||||
}, pm)
|
}, pm)
|
||||||
assert.True(t, ctx.HasReadPerm("has_no_perm_project"))
|
assert.True(t, ctx.HasReadPerm("private_project"))
|
||||||
|
|
||||||
|
// non-exist project, authenticated, system admin
|
||||||
|
ctx = NewSecurityContext(&models.User{
|
||||||
|
Username: "test",
|
||||||
|
HasAdminRole: 1,
|
||||||
|
}, pm)
|
||||||
|
assert.False(t, ctx.HasReadPerm("non_exist_project"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHasWritePerm(t *testing.T) {
|
func TestHasWritePerm(t *testing.T) {
|
||||||
pm := &fakePM{
|
pm := &fakePM{
|
||||||
|
projects: []*models.Project{read, write, private},
|
||||||
roles: map[string][]int{
|
roles: map[string][]int{
|
||||||
"has_read_perm_project": []int{common.RoleGuest},
|
"has_read_perm_project": []int{common.RoleGuest},
|
||||||
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
|
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
|
||||||
@ -137,6 +192,12 @@ func TestHasWritePerm(t *testing.T) {
|
|||||||
ctx := NewSecurityContext(nil, pm)
|
ctx := NewSecurityContext(nil, pm)
|
||||||
assert.False(t, ctx.HasWritePerm("has_write_perm_project"))
|
assert.False(t, ctx.HasWritePerm("has_write_perm_project"))
|
||||||
|
|
||||||
|
// authenticated, non-exist project
|
||||||
|
ctx = NewSecurityContext(&models.User{
|
||||||
|
Username: "test",
|
||||||
|
}, pm)
|
||||||
|
assert.False(t, ctx.HasWritePerm("non_exist_project"))
|
||||||
|
|
||||||
// authenticated, has read perm
|
// authenticated, has read perm
|
||||||
ctx = NewSecurityContext(&models.User{
|
ctx = NewSecurityContext(&models.User{
|
||||||
Username: "test",
|
Username: "test",
|
||||||
@ -154,11 +215,19 @@ func TestHasWritePerm(t *testing.T) {
|
|||||||
Username: "test",
|
Username: "test",
|
||||||
HasAdminRole: 1,
|
HasAdminRole: 1,
|
||||||
}, pm)
|
}, pm)
|
||||||
assert.True(t, ctx.HasReadPerm("has_no_perm_project"))
|
assert.True(t, ctx.HasReadPerm("private_project"))
|
||||||
|
|
||||||
|
// authenticated, system admin, non-exist project
|
||||||
|
ctx = NewSecurityContext(&models.User{
|
||||||
|
Username: "test",
|
||||||
|
HasAdminRole: 1,
|
||||||
|
}, pm)
|
||||||
|
assert.False(t, ctx.HasReadPerm("non_exist_project"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHasAllPerm(t *testing.T) {
|
func TestHasAllPerm(t *testing.T) {
|
||||||
pm := &fakePM{
|
pm := &fakePM{
|
||||||
|
projects: []*models.Project{read, write, all, private},
|
||||||
roles: map[string][]int{
|
roles: map[string][]int{
|
||||||
"has_read_perm_project": []int{common.RoleGuest},
|
"has_read_perm_project": []int{common.RoleGuest},
|
||||||
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
|
"has_write_perm_project": []int{common.RoleGuest, common.RoleDeveloper},
|
||||||
@ -170,6 +239,12 @@ func TestHasAllPerm(t *testing.T) {
|
|||||||
ctx := NewSecurityContext(nil, pm)
|
ctx := NewSecurityContext(nil, pm)
|
||||||
assert.False(t, ctx.HasAllPerm("has_all_perm_project"))
|
assert.False(t, ctx.HasAllPerm("has_all_perm_project"))
|
||||||
|
|
||||||
|
// authenticated, non-exist project
|
||||||
|
ctx = NewSecurityContext(&models.User{
|
||||||
|
Username: "test",
|
||||||
|
}, pm)
|
||||||
|
assert.False(t, ctx.HasAllPerm("non_exist_project"))
|
||||||
|
|
||||||
// authenticated, has read perm
|
// authenticated, has read perm
|
||||||
ctx = NewSecurityContext(&models.User{
|
ctx = NewSecurityContext(&models.User{
|
||||||
Username: "test",
|
Username: "test",
|
||||||
@ -193,5 +268,12 @@ func TestHasAllPerm(t *testing.T) {
|
|||||||
Username: "test",
|
Username: "test",
|
||||||
HasAdminRole: 1,
|
HasAdminRole: 1,
|
||||||
}, pm)
|
}, pm)
|
||||||
assert.True(t, ctx.HasReadPerm("has_no_perm_project"))
|
assert.True(t, ctx.HasAllPerm("private_project"))
|
||||||
|
|
||||||
|
// authenticated, system admin, non-exist project
|
||||||
|
ctx = NewSecurityContext(&models.User{
|
||||||
|
Username: "test",
|
||||||
|
HasAdminRole: 1,
|
||||||
|
}, pm)
|
||||||
|
assert.False(t, ctx.HasAllPerm("non_exist_project"))
|
||||||
}
|
}
|
||||||
|
@ -34,21 +34,20 @@ type BaseController struct {
|
|||||||
ProjectMgr projectmanager.ProjectManager
|
ProjectMgr projectmanager.ProjectManager
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare inits security context and project manager from beego
|
// Prepare inits security context and project manager from request
|
||||||
// context
|
// context
|
||||||
func (b *BaseController) Prepare() {
|
func (b *BaseController) Prepare() {
|
||||||
ok := false
|
ctx, err := filter.GetSecurityContext(b.Ctx.Request)
|
||||||
ctx := b.Ctx.Input.GetData(filter.HarborSecurityContext)
|
if err != nil {
|
||||||
b.SecurityCtx, ok = ctx.(security.Context)
|
|
||||||
if !ok {
|
|
||||||
log.Error("failed to get security context")
|
log.Error("failed to get security context")
|
||||||
b.CustomAbort(http.StatusInternalServerError, "")
|
b.CustomAbort(http.StatusInternalServerError, "")
|
||||||
}
|
}
|
||||||
|
b.SecurityCtx = ctx
|
||||||
|
|
||||||
pm := b.Ctx.Input.GetData(filter.HarborProjectManager)
|
pm, err := filter.GetProjectManager(b.Ctx.Request)
|
||||||
b.ProjectMgr, ok = pm.(projectmanager.ProjectManager)
|
if err != nil {
|
||||||
if !ok {
|
|
||||||
log.Error("failed to get project manager")
|
log.Error("failed to get project manager")
|
||||||
b.CustomAbort(http.StatusInternalServerError, "")
|
b.CustomAbort(http.StatusInternalServerError, "")
|
||||||
}
|
}
|
||||||
|
b.ProjectMgr = pm
|
||||||
}
|
}
|
||||||
|
@ -356,7 +356,7 @@ func diffRepos(reposInRegistry []string, reposInDB []string) ([]string, []string
|
|||||||
return needsAdd, needsDel, err
|
return needsAdd, needsDel, err
|
||||||
}
|
}
|
||||||
client, err := NewRepositoryClient(endpoint, true,
|
client, err := NewRepositoryClient(endpoint, true,
|
||||||
"admin", repoInR, "repository", repoInR)
|
"admin", repoInR, "repository", repoInR, "pull")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return needsAdd, needsDel, err
|
return needsAdd, needsDel, err
|
||||||
}
|
}
|
||||||
@ -381,7 +381,7 @@ func diffRepos(reposInRegistry []string, reposInDB []string) ([]string, []string
|
|||||||
return needsAdd, needsDel, err
|
return needsAdd, needsDel, err
|
||||||
}
|
}
|
||||||
client, err := NewRepositoryClient(endpoint, true,
|
client, err := NewRepositoryClient(endpoint, true,
|
||||||
"admin", repoInR, "repository", repoInR)
|
"admin", repoInR, "repository", repoInR, "pull")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return needsAdd, needsDel, err
|
return needsAdd, needsDel, err
|
||||||
}
|
}
|
||||||
@ -428,6 +428,7 @@ func projectExists(repository string) (bool, error) {
|
|||||||
return dao.ProjectExists(project)
|
return dao.ProjectExists(project)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO need a registry client which accept a raw token as param
|
||||||
func initRegistryClient() (r *registry.Registry, err error) {
|
func initRegistryClient() (r *registry.Registry, err error) {
|
||||||
endpoint, err := config.RegistryURL()
|
endpoint, err := config.RegistryURL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -500,6 +501,7 @@ func repositoryExist(name string, client *registry.Repository) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRegistryClient ...
|
// NewRegistryClient ...
|
||||||
|
// TODO need a registry client which accept a raw token as param
|
||||||
func NewRegistryClient(endpoint string, insecure bool, username, scopeType, scopeName string,
|
func NewRegistryClient(endpoint string, insecure bool, username, scopeType, scopeName string,
|
||||||
scopeActions ...string) (*registry.Registry, error) {
|
scopeActions ...string) (*registry.Registry, error) {
|
||||||
authorizer := auth.NewRegistryUsernameTokenAuthorizer(username, scopeType, scopeName, scopeActions...)
|
authorizer := auth.NewRegistryUsernameTokenAuthorizer(username, scopeType, scopeName, scopeActions...)
|
||||||
@ -517,6 +519,7 @@ func NewRegistryClient(endpoint string, insecure bool, username, scopeType, scop
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewRepositoryClient ...
|
// NewRepositoryClient ...
|
||||||
|
// TODO need a registry client which accept a raw token as param
|
||||||
func NewRepositoryClient(endpoint string, insecure bool, username, repository, scopeType, scopeName string,
|
func NewRepositoryClient(endpoint string, insecure bool, username, repository, scopeType, scopeName string,
|
||||||
scopeActions ...string) (*registry.Repository, error) {
|
scopeActions ...string) (*registry.Repository, error) {
|
||||||
|
|
||||||
|
@ -15,11 +15,15 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
beegoctx "github.com/astaxie/beego/context"
|
beegoctx "github.com/astaxie/beego/context"
|
||||||
"github.com/vmware/harbor/src/common"
|
"github.com/vmware/harbor/src/common"
|
||||||
"github.com/vmware/harbor/src/common/models"
|
"github.com/vmware/harbor/src/common/models"
|
||||||
|
"github.com/vmware/harbor/src/common/security"
|
||||||
"github.com/vmware/harbor/src/common/security/rbac"
|
"github.com/vmware/harbor/src/common/security/rbac"
|
||||||
"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"
|
||||||
@ -28,15 +32,17 @@ import (
|
|||||||
"github.com/vmware/harbor/src/ui/projectmanager"
|
"github.com/vmware/harbor/src/ui/projectmanager"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type key string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// HarborSecurityContext is the name of security context passed to handlers
|
// HarborSecurityContext is the name of security context passed to handlers
|
||||||
HarborSecurityContext = "harbor_security_context"
|
HarborSecurityContext key = "harbor_security_context"
|
||||||
// HarborProjectManager is the name of project manager passed to handlers
|
// HarborProjectManager is the name of project manager passed to handlers
|
||||||
HarborProjectManager = "harbor_project_manager"
|
HarborProjectManager key = "harbor_project_manager"
|
||||||
)
|
)
|
||||||
|
|
||||||
// SecurityFilter authenticates the request and passes a security context with it
|
// SecurityFilter authenticates the request and passes a security context
|
||||||
// which can be used to do some authorization
|
// 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) {
|
||||||
if ctx == nil {
|
if ctx == nil {
|
||||||
return
|
return
|
||||||
@ -60,13 +66,16 @@ func fillContext(ctx *beegoctx.Context) {
|
|||||||
// secret
|
// secret
|
||||||
scrt := ctx.GetCookie("secret")
|
scrt := ctx.GetCookie("secret")
|
||||||
if len(scrt) != 0 {
|
if len(scrt) != 0 {
|
||||||
ctx.Input.SetData(HarborProjectManager,
|
ct := context.WithValue(ctx.Request.Context(),
|
||||||
|
HarborProjectManager,
|
||||||
getProjectManager(ctx))
|
getProjectManager(ctx))
|
||||||
|
|
||||||
log.Info("creating a secret security context...")
|
log.Info("creating a secret security context...")
|
||||||
ctx.Input.SetData(HarborSecurityContext,
|
ct = context.WithValue(ct, HarborSecurityContext,
|
||||||
secret.NewSecurityContext(scrt, config.SecretStore))
|
secret.NewSecurityContext(scrt, config.SecretStore))
|
||||||
|
|
||||||
|
ctx.Request = ctx.Request.WithContext(ct)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,11 +122,12 @@ func fillContext(ctx *beegoctx.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pm := getProjectManager(ctx)
|
pm := getProjectManager(ctx)
|
||||||
ctx.Input.SetData(HarborProjectManager, pm)
|
ct := context.WithValue(ctx.Request.Context(), HarborProjectManager, pm)
|
||||||
|
|
||||||
log.Info("creating a rbac security context...")
|
log.Info("creating a rbac security context...")
|
||||||
ctx.Input.SetData(HarborSecurityContext,
|
ct = context.WithValue(ct, HarborSecurityContext,
|
||||||
rbac.NewSecurityContext(user, pm))
|
rbac.NewSecurityContext(user, pm))
|
||||||
|
ctx.Request = ctx.Request.WithContext(ct)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -133,3 +143,41 @@ func getProjectManager(ctx *beegoctx.Context) projectmanager.ProjectManager {
|
|||||||
log.Info("filling a project manager based on pms...")
|
log.Info("filling a project manager based on pms...")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetSecurityContext tries to get security context from request and returns it
|
||||||
|
func GetSecurityContext(req *http.Request) (security.Context, error) {
|
||||||
|
if req == nil {
|
||||||
|
return nil, fmt.Errorf("request is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := req.Context().Value(HarborSecurityContext)
|
||||||
|
if ctx == nil {
|
||||||
|
return nil, fmt.Errorf("the security context got from request is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
c, ok := ctx.(security.Context)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("the variable got from request is not security context type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return c, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetProjectManager tries to get project manager from request and returns it
|
||||||
|
func GetProjectManager(req *http.Request) (projectmanager.ProjectManager, error) {
|
||||||
|
if req == nil {
|
||||||
|
return nil, fmt.Errorf("request is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
pm := req.Context().Value(HarborProjectManager)
|
||||||
|
if pm == nil {
|
||||||
|
return nil, fmt.Errorf("the project manager got from request is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
p, ok := pm.(projectmanager.ProjectManager)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("the variable got from request is not project manager type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return p, nil
|
||||||
|
}
|
||||||
|
@ -15,6 +15,7 @@
|
|||||||
package filter
|
package filter
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -26,7 +27,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/astaxie/beego"
|
"github.com/astaxie/beego"
|
||||||
"github.com/astaxie/beego/context"
|
beegoctx "github.com/astaxie/beego/context"
|
||||||
"github.com/astaxie/beego/session"
|
"github.com/astaxie/beego/session"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/dao"
|
||||||
@ -36,6 +37,8 @@ import (
|
|||||||
_ "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"
|
||||||
"github.com/vmware/harbor/src/ui/config"
|
"github.com/vmware/harbor/src/ui/config"
|
||||||
|
"github.com/vmware/harbor/src/ui/projectmanager"
|
||||||
|
"github.com/vmware/harbor/src/ui/projectmanager/db"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@ -209,9 +212,9 @@ func TestFillContext(t *testing.T) {
|
|||||||
assert.NotNil(t, projectManager(ctx))
|
assert.NotNil(t, projectManager(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
func newContext(req *http.Request) (*context.Context, error) {
|
func newContext(req *http.Request) (*beegoctx.Context, error) {
|
||||||
var err error
|
var err error
|
||||||
ctx := context.NewContext()
|
ctx := beegoctx.NewContext()
|
||||||
ctx.Reset(httptest.NewRecorder(), req)
|
ctx.Reset(httptest.NewRecorder(), req)
|
||||||
if req != nil {
|
if req != nil {
|
||||||
ctx.Input.CruSession, err = beego.GlobalSessions.SessionStart(ctx.ResponseWriter, req)
|
ctx.Input.CruSession, err = beego.GlobalSessions.SessionStart(ctx.ResponseWriter, req)
|
||||||
@ -235,10 +238,77 @@ func addSessionIDToCookie(req *http.Request, sessionID string) {
|
|||||||
req.AddCookie(cookie)
|
req.AddCookie(cookie)
|
||||||
}
|
}
|
||||||
|
|
||||||
func securityContext(ctx *context.Context) interface{} {
|
func securityContext(ctx *beegoctx.Context) interface{} {
|
||||||
return ctx.Input.Data()[HarborSecurityContext]
|
c, err := GetSecurityContext(ctx.Request)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
func projectManager(ctx *context.Context) interface{} {
|
func projectManager(ctx *beegoctx.Context) interface{} {
|
||||||
return ctx.Input.Data()[HarborProjectManager]
|
if ctx.Request == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ctx.Request.Context().Value(HarborProjectManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetSecurityContext(t *testing.T) {
|
||||||
|
// nil request
|
||||||
|
ctx, err := GetSecurityContext(nil)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
// the request contains no security context
|
||||||
|
req, err := http.NewRequest("", "", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
ctx, err = GetSecurityContext(req)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
// the request contains a variable which is not the correct type
|
||||||
|
req, err = http.NewRequest("", "", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
|
HarborSecurityContext, "test"))
|
||||||
|
ctx, err = GetSecurityContext(req)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
// the request contains a correct variable
|
||||||
|
req, err = http.NewRequest("", "", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
|
HarborSecurityContext, rbac.NewSecurityContext(nil, nil)))
|
||||||
|
ctx, err = GetSecurityContext(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
_, ok := ctx.(security.Context)
|
||||||
|
assert.True(t, ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetProjectManager(t *testing.T) {
|
||||||
|
// nil request
|
||||||
|
pm, err := GetProjectManager(nil)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
// the request contains no project manager
|
||||||
|
req, err := http.NewRequest("", "", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
pm, err = GetProjectManager(req)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
// the request contains a variable which is not the correct type
|
||||||
|
req, err = http.NewRequest("", "", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
|
HarborProjectManager, "test"))
|
||||||
|
pm, err = GetProjectManager(req)
|
||||||
|
assert.NotNil(t, err)
|
||||||
|
|
||||||
|
// the request contains a correct variable
|
||||||
|
req, err = http.NewRequest("", "", nil)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
req = req.WithContext(context.WithValue(req.Context(),
|
||||||
|
HarborProjectManager, &db.ProjectManager{}))
|
||||||
|
pm, err = GetProjectManager(req)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
_, ok := pm.(projectmanager.ProjectManager)
|
||||||
|
assert.True(t, ok)
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
"github.com/vmware/harbor/src/common/security"
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
"github.com/vmware/harbor/src/ui/config"
|
"github.com/vmware/harbor/src/ui/config"
|
||||||
|
|
||||||
@ -74,7 +74,8 @@ func GetResourceActions(scopes []string) []*token.ResourceActions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//filterAccess iterate a list of resource actions and try to use the filter that matches the resource type to filter the actions.
|
//filterAccess iterate a list of resource actions and try to use the filter that matches the resource type to filter the actions.
|
||||||
func filterAccess(access []*token.ResourceActions, u userInfo, filters map[string]accessFilter) error {
|
func filterAccess(access []*token.ResourceActions, ctx security.Context,
|
||||||
|
filters map[string]accessFilter) error {
|
||||||
var err error
|
var err error
|
||||||
for _, a := range access {
|
for _, a := range access {
|
||||||
f, ok := filters[a.Type]
|
f, ok := filters[a.Type]
|
||||||
@ -83,8 +84,8 @@ func filterAccess(access []*token.ResourceActions, u userInfo, filters map[strin
|
|||||||
log.Warningf("No filter found for access type: %s, skip filter, the access of resource '%s' will be set empty.", a.Type, a.Name)
|
log.Warningf("No filter found for access type: %s, skip filter, the access of resource '%s' will be set empty.", a.Type, a.Name)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = f.filter(u, a)
|
err = f.filter(ctx, a)
|
||||||
log.Debugf("user: %s, access: %v", u.name, a)
|
log.Debugf("user: %s, access: %v", ctx.GetUsername(), a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -92,31 +93,23 @@ func filterAccess(access []*token.ResourceActions, u userInfo, filters map[strin
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO merge RegistryTokenForUI NotaryTokenForUI genTokenForUI
|
||||||
|
// to one function
|
||||||
|
|
||||||
//RegistryTokenForUI calls genTokenForUI to get raw token for registry
|
//RegistryTokenForUI calls genTokenForUI to get raw token for registry
|
||||||
func RegistryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) {
|
func RegistryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) {
|
||||||
return genTokenForUI(username, service, scopes, registryFilterMap)
|
return genTokenForUI(username, service, scopes)
|
||||||
}
|
}
|
||||||
|
|
||||||
//NotaryTokenForUI calls genTokenForUI to get raw token for notary
|
//NotaryTokenForUI calls genTokenForUI to get raw token for notary
|
||||||
func NotaryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) {
|
func NotaryTokenForUI(username string, service string, scopes []string) (string, int, *time.Time, error) {
|
||||||
return genTokenForUI(username, service, scopes, notaryFilterMap)
|
return genTokenForUI(username, service, scopes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// genTokenForUI is for the UI process to call, so it won't establish a https connection from UI to proxy.
|
// genTokenForUI is for the UI process to call, so it won't establish a https connection from UI to proxy.
|
||||||
func genTokenForUI(username string, service string, scopes []string, filters map[string]accessFilter) (string, int, *time.Time, error) {
|
func genTokenForUI(username string, service string,
|
||||||
isAdmin, err := dao.IsAdminRole(username)
|
scopes []string) (string, int, *time.Time, error) {
|
||||||
if err != nil {
|
|
||||||
return "", 0, nil, err
|
|
||||||
}
|
|
||||||
u := userInfo{
|
|
||||||
name: username,
|
|
||||||
allPerm: isAdmin,
|
|
||||||
}
|
|
||||||
access := GetResourceActions(scopes)
|
access := GetResourceActions(scopes)
|
||||||
err = filterAccess(access, u, filters)
|
|
||||||
if err != nil {
|
|
||||||
return "", 0, nil, err
|
|
||||||
}
|
|
||||||
return MakeRawToken(username, service, access)
|
return MakeRawToken(username, service, access)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,12 +16,14 @@ package token
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/docker/distribution/registry/auth/token"
|
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
|
||||||
"github.com/vmware/harbor/src/ui/config"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/docker/distribution/registry/auth/token"
|
||||||
|
"github.com/vmware/harbor/src/common/security"
|
||||||
|
"github.com/vmware/harbor/src/common/utils/log"
|
||||||
|
"github.com/vmware/harbor/src/ui/config"
|
||||||
|
"github.com/vmware/harbor/src/ui/filter"
|
||||||
)
|
)
|
||||||
|
|
||||||
var creatorMap map[string]Creator
|
var creatorMap map[string]Creator
|
||||||
@ -54,19 +56,12 @@ func InitCreators() {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
creatorMap[notary] = &generalCreator{
|
creatorMap[notary] = &generalCreator{
|
||||||
validators: []ReqValidator{
|
|
||||||
&basicAuthValidator{},
|
|
||||||
},
|
|
||||||
service: notary,
|
service: notary,
|
||||||
filterMap: notaryFilterMap,
|
filterMap: notaryFilterMap,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
creatorMap[registry] = &generalCreator{
|
creatorMap[registry] = &generalCreator{
|
||||||
validators: []ReqValidator{
|
|
||||||
&secretValidator{config.JobserviceSecret()},
|
|
||||||
&basicAuthValidator{},
|
|
||||||
},
|
|
||||||
service: registry,
|
service: registry,
|
||||||
filterMap: registryFilterMap,
|
filterMap: registryFilterMap,
|
||||||
}
|
}
|
||||||
@ -127,18 +122,19 @@ func parseImg(s string) (*image, error) {
|
|||||||
|
|
||||||
// An accessFilter will filter access based on userinfo
|
// An accessFilter will filter access based on userinfo
|
||||||
type accessFilter interface {
|
type accessFilter interface {
|
||||||
filter(user userInfo, a *token.ResourceActions) error
|
filter(ctx security.Context, a *token.ResourceActions) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type registryFilter struct {
|
type registryFilter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (reg registryFilter) filter(user userInfo, a *token.ResourceActions) error {
|
func (reg registryFilter) filter(ctx security.Context,
|
||||||
|
a *token.ResourceActions) error {
|
||||||
//Do not filter if the request is to access registry catalog
|
//Do not filter if the request is to access registry catalog
|
||||||
if a.Name != "catalog" {
|
if a.Name != "catalog" {
|
||||||
return fmt.Errorf("Unable to handle, type: %s, name: %s", a.Type, a.Name)
|
return fmt.Errorf("Unable to handle, type: %s, name: %s", a.Type, a.Name)
|
||||||
}
|
}
|
||||||
if !user.allPerm {
|
if !ctx.IsSysAdmin() {
|
||||||
//Set the actions to empty is the user is not admin
|
//Set the actions to empty is the user is not admin
|
||||||
a.Actions = []string{}
|
a.Actions = []string{}
|
||||||
}
|
}
|
||||||
@ -150,7 +146,7 @@ type repositoryFilter struct {
|
|||||||
parser imageParser
|
parser imageParser
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rep repositoryFilter) filter(user userInfo, a *token.ResourceActions) error {
|
func (rep repositoryFilter) filter(ctx security.Context, a *token.ResourceActions) error {
|
||||||
//clear action list to assign to new acess element after perm check.
|
//clear action list to assign to new acess element after perm check.
|
||||||
img, err := rep.parser.parse(a.Name)
|
img, err := rep.parser.parse(a.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -158,37 +154,21 @@ func (rep repositoryFilter) filter(user userInfo, a *token.ResourceActions) erro
|
|||||||
}
|
}
|
||||||
project := img.namespace
|
project := img.namespace
|
||||||
permission := ""
|
permission := ""
|
||||||
if user.allPerm {
|
if ctx.HasAllPerm(project) {
|
||||||
exist, err := dao.ProjectExists(project)
|
permission = "RWM"
|
||||||
if err != nil {
|
} else if ctx.HasWritePerm(project) {
|
||||||
log.Errorf("Error occurred in CheckExistProject: %v", err)
|
permission = "RW"
|
||||||
//just leave empty permission
|
} else if ctx.HasReadPerm(project) {
|
||||||
return nil
|
permission = "R"
|
||||||
}
|
|
||||||
if exist {
|
|
||||||
permission = "RWM"
|
|
||||||
} else {
|
|
||||||
log.Infof("project %s does not exist, set empty permission for admin\n", project)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
permission, err = dao.GetPermission(user.name, project)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error occurred in GetPermission: %v", err)
|
|
||||||
//just leave empty permission
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if dao.IsProjectPublic(project) {
|
|
||||||
permission += "R"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.Actions = permToActions(permission)
|
a.Actions = permToActions(permission)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type generalCreator struct {
|
type generalCreator struct {
|
||||||
validators []ReqValidator
|
service string
|
||||||
service string
|
filterMap map[string]accessFilter
|
||||||
filterMap map[string]accessFilter
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type unauthorizedError struct{}
|
type unauthorizedError struct{}
|
||||||
@ -198,7 +178,6 @@ func (e *unauthorizedError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g generalCreator) Create(r *http.Request) (*tokenJSON, error) {
|
func (g generalCreator) Create(r *http.Request) (*tokenJSON, error) {
|
||||||
var user *userInfo
|
|
||||||
var err error
|
var err error
|
||||||
var scopes []string
|
var scopes []string
|
||||||
scopeParm := r.URL.Query()["scope"]
|
scopeParm := r.URL.Query()["scope"]
|
||||||
@ -206,25 +185,22 @@ func (g generalCreator) Create(r *http.Request) (*tokenJSON, error) {
|
|||||||
scopes = strings.Split(r.URL.Query()["scope"][0], " ")
|
scopes = strings.Split(r.URL.Query()["scope"][0], " ")
|
||||||
}
|
}
|
||||||
log.Debugf("scopes: %v", scopes)
|
log.Debugf("scopes: %v", scopes)
|
||||||
for _, v := range g.validators {
|
|
||||||
user, err = v.validate(r)
|
ctx, err := filter.GetSecurityContext(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to get security context from request")
|
||||||
}
|
|
||||||
if user != nil {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if user == nil {
|
|
||||||
|
// for docker login
|
||||||
|
if !ctx.IsAuthenticated() {
|
||||||
if len(scopes) == 0 {
|
if len(scopes) == 0 {
|
||||||
return nil, &unauthorizedError{}
|
return nil, &unauthorizedError{}
|
||||||
}
|
}
|
||||||
user = &userInfo{}
|
|
||||||
}
|
}
|
||||||
access := GetResourceActions(scopes)
|
access := GetResourceActions(scopes)
|
||||||
err = filterAccess(access, *user, g.filterMap)
|
err = filterAccess(access, ctx, g.filterMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return makeToken(user.name, g.service, access)
|
return makeToken(ctx.GetUsername(), g.service, access)
|
||||||
}
|
}
|
||||||
|
@ -22,13 +22,14 @@ import (
|
|||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/vmware/harbor/src/common/utils/test"
|
|
||||||
"github.com/vmware/harbor/src/ui/config"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/vmware/harbor/src/common/utils/test"
|
||||||
|
"github.com/vmware/harbor/src/ui/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMain(m *testing.M) {
|
func TestMain(m *testing.M) {
|
||||||
@ -199,6 +200,31 @@ func TestEndpointParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fakeSecurityContext struct {
|
||||||
|
isAdmin bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeSecurityContext) IsAuthenticated() bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeSecurityContext) GetUsername() string {
|
||||||
|
return "jack"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeSecurityContext) IsSysAdmin() bool {
|
||||||
|
return f.isAdmin
|
||||||
|
}
|
||||||
|
func (f *fakeSecurityContext) HasReadPerm(projectIDOrName interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (f *fakeSecurityContext) HasWritePerm(projectIDOrName interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
func (f *fakeSecurityContext) HasAllPerm(projectIDOrName interface{}) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func TestFilterAccess(t *testing.T) {
|
func TestFilterAccess(t *testing.T) {
|
||||||
//TODO put initial data in DB to verify repository filter.
|
//TODO put initial data in DB to verify repository filter.
|
||||||
var err error
|
var err error
|
||||||
@ -206,8 +232,7 @@ func TestFilterAccess(t *testing.T) {
|
|||||||
a1 := GetResourceActions(s)
|
a1 := GetResourceActions(s)
|
||||||
a2 := GetResourceActions(s)
|
a2 := GetResourceActions(s)
|
||||||
a3 := GetResourceActions(s)
|
a3 := GetResourceActions(s)
|
||||||
u1 := userInfo{"jack", true}
|
|
||||||
u2 := userInfo{"jack", false}
|
|
||||||
ra1 := token.ResourceActions{
|
ra1 := token.ResourceActions{
|
||||||
Type: "registry",
|
Type: "registry",
|
||||||
Name: "catalog",
|
Name: "catalog",
|
||||||
@ -218,13 +243,21 @@ func TestFilterAccess(t *testing.T) {
|
|||||||
Name: "catalog",
|
Name: "catalog",
|
||||||
Actions: []string{},
|
Actions: []string{},
|
||||||
}
|
}
|
||||||
err = filterAccess(a1, u1, registryFilterMap)
|
err = filterAccess(a1, &fakeSecurityContext{
|
||||||
|
isAdmin: true,
|
||||||
|
}, registryFilterMap)
|
||||||
assert.Nil(t, err, "Unexpected error: %v", err)
|
assert.Nil(t, err, "Unexpected error: %v", err)
|
||||||
assert.Equal(t, ra1, *a1[0], "Mismatch after registry filter Map")
|
assert.Equal(t, ra1, *a1[0], "Mismatch after registry filter Map")
|
||||||
err = filterAccess(a2, u1, notaryFilterMap)
|
|
||||||
|
err = filterAccess(a2, &fakeSecurityContext{
|
||||||
|
isAdmin: true,
|
||||||
|
}, notaryFilterMap)
|
||||||
assert.Nil(t, err, "Unexpected error: %v", err)
|
assert.Nil(t, err, "Unexpected error: %v", err)
|
||||||
assert.Equal(t, ra2, *a2[0], "Mismatch after notary filter Map")
|
assert.Equal(t, ra2, *a2[0], "Mismatch after notary filter Map")
|
||||||
err = filterAccess(a3, u2, registryFilterMap)
|
|
||||||
|
err = filterAccess(a3, &fakeSecurityContext{
|
||||||
|
isAdmin: false,
|
||||||
|
}, registryFilterMap)
|
||||||
assert.Nil(t, err, "Unexpected error: %v", err)
|
assert.Nil(t, err, "Unexpected error: %v", err)
|
||||||
assert.Equal(t, ra2, *a3[0], "Mismatch after registry filter Map")
|
assert.Equal(t, ra2, *a3[0], "Mismatch after registry filter Map")
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
// 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 token
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/vmware/harbor/src/common/dao"
|
|
||||||
"github.com/vmware/harbor/src/common/models"
|
|
||||||
"github.com/vmware/harbor/src/common/utils/log"
|
|
||||||
"github.com/vmware/harbor/src/ui/auth"
|
|
||||||
svc_utils "github.com/vmware/harbor/src/ui/service/utils"
|
|
||||||
"net/http"
|
|
||||||
)
|
|
||||||
|
|
||||||
//For filtering permission by token creators.
|
|
||||||
type userInfo struct {
|
|
||||||
name string
|
|
||||||
allPerm bool
|
|
||||||
}
|
|
||||||
|
|
||||||
//ReqValidator validates request based on different rules and returns userInfo
|
|
||||||
type ReqValidator interface {
|
|
||||||
validate(req *http.Request) (*userInfo, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type secretValidator struct {
|
|
||||||
secret string
|
|
||||||
}
|
|
||||||
|
|
||||||
var jobServiceUserInfo userInfo
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
jobServiceUserInfo = userInfo{
|
|
||||||
name: "job-service-user",
|
|
||||||
allPerm: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sv secretValidator) validate(r *http.Request) (*userInfo, error) {
|
|
||||||
if svc_utils.VerifySecret(r, sv.secret) {
|
|
||||||
return &jobServiceUserInfo, nil
|
|
||||||
}
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type basicAuthValidator struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ba basicAuthValidator) validate(r *http.Request) (*userInfo, error) {
|
|
||||||
uid, password, _ := r.BasicAuth()
|
|
||||||
user, err := auth.Login(models.AuthModel{
|
|
||||||
Principal: uid,
|
|
||||||
Password: password,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error occurred in UserLogin: %v", err)
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if user == nil {
|
|
||||||
log.Warningf("Invalid credentials for uid: %s", uid)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
isAdmin, err := dao.IsAdminRole(user.UserID)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Error occurred in IsAdminRole: %v", err)
|
|
||||||
}
|
|
||||||
info := &userInfo{
|
|
||||||
name: user.Username,
|
|
||||||
allPerm: isAdmin,
|
|
||||||
}
|
|
||||||
return info, nil
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user