From e08b8217c366bb90448d2696e4276f04b54e7a1e Mon Sep 17 00:00:00 2001 From: Wenkai Yin Date: Thu, 27 Apr 2017 16:26:38 +0800 Subject: [PATCH] implement security context for secret --- src/common/secret/store.go | 48 +++++++ src/common/secret/store_test.go | 39 +++++ src/{ui => common}/security/context.go | 11 +- src/{ui => common}/security/db/context.go | 0 src/{ui => common}/security/rbac/context.go | 0 src/common/security/secret/context.go | 74 ++++++++++ src/common/security/secret/context_test.go | 134 ++++++++++++++++++ src/ui/filter/security.go | 2 +- .../secret/context.go => pms/db/service.go} | 2 +- src/ui/pms/service.go | 20 +++ 10 files changed, 322 insertions(+), 8 deletions(-) create mode 100644 src/common/secret/store.go create mode 100644 src/common/secret/store_test.go rename src/{ui => common}/security/context.go (83%) rename src/{ui => common}/security/db/context.go (100%) rename src/{ui => common}/security/rbac/context.go (100%) create mode 100644 src/common/security/secret/context.go create mode 100644 src/common/security/secret/context_test.go rename src/ui/{security/secret/context.go => pms/db/service.go} (97%) create mode 100644 src/ui/pms/service.go diff --git a/src/common/secret/store.go b/src/common/secret/store.go new file mode 100644 index 000000000..7a0a10d7f --- /dev/null +++ b/src/common/secret/store.go @@ -0,0 +1,48 @@ +// 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 secret + +const ( + // AdminserverUser is the name of adminserver user + AdminserverUser = "harbor-adminserver" + // JobserviceUser is the name of jobservice user + JobserviceUser = "harbor-jobservice" + // UIUser is the name of ui user + UIUser = "harbor-ui" +) + +// Store the secrets and provides methods to validate secrets +type Store struct { + // the key is secret + // the value is username + secrets map[string]string +} + +// NewStore ... +func NewStore(secrets map[string]string) *Store { + return &Store{ + secrets: secrets, + } +} + +// IsValid returns whether the secret is valid +func (s *Store) IsValid(secret string) bool { + return len(s.GetUsername(secret)) != 0 +} + +// GetUsername returns the corresponding username of the secret +func (s *Store) GetUsername(secret string) string { + return s.secrets[secret] +} diff --git a/src/common/secret/store_test.go b/src/common/secret/store_test.go new file mode 100644 index 000000000..1250da282 --- /dev/null +++ b/src/common/secret/store_test.go @@ -0,0 +1,39 @@ +// 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 secret + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestIsValid(t *testing.T) { + store := NewStore(map[string]string{ + "secret1": "username1", + }) + + assert.False(t, store.IsValid("invalid_secret")) + assert.True(t, store.IsValid("secret1")) +} + +func TestGetUsername(t *testing.T) { + store := NewStore(map[string]string{ + "secret1": "username1", + }) + + assert.Equal(t, "", store.GetUsername("invalid_secret")) + assert.Equal(t, "username1", store.GetUsername("secret1")) +} diff --git a/src/ui/security/context.go b/src/common/security/context.go similarity index 83% rename from src/ui/security/context.go rename to src/common/security/context.go index a112523f1..3a75a8d25 100644 --- a/src/ui/security/context.go +++ b/src/common/security/context.go @@ -18,15 +18,14 @@ package security type Context interface { // IsAuthenticated returns whether the context has been authenticated or not IsAuthenticated() bool + // GetUsername returns the username of user related to the context + GetUsername() string // IsSysAdmin returns whether the user is system admin IsSysAdmin() bool // HasReadPerm returns whether the user has read permission to the project - // whose ID is projectID - HasReadPerm(projectID int64) bool + HasReadPerm(projectIDOrName interface{}) bool // HasWritePerm returns whether the user has write permission to the project - // whose ID is projectID - HasWritePerm(projectID int64) bool + HasWritePerm(projectIDOrName interface{}) bool // HasAllPerm returns whether the user has all permissions to the project - // whose ID is projectID - HasAllPerm(projectID int64) bool + HasAllPerm(projectIDOrName interface{}) bool } diff --git a/src/ui/security/db/context.go b/src/common/security/db/context.go similarity index 100% rename from src/ui/security/db/context.go rename to src/common/security/db/context.go diff --git a/src/ui/security/rbac/context.go b/src/common/security/rbac/context.go similarity index 100% rename from src/ui/security/rbac/context.go rename to src/common/security/rbac/context.go diff --git a/src/common/security/secret/context.go b/src/common/security/secret/context.go new file mode 100644 index 000000000..3b24afa50 --- /dev/null +++ b/src/common/security/secret/context.go @@ -0,0 +1,74 @@ +// 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 secret + +import ( + "github.com/vmware/harbor/src/common/secret" +) + +// SecurityContext implements security.Context interface based on secret store +type SecurityContext struct { + secret string + store *secret.Store +} + +// NewSecurityContext ... +func NewSecurityContext(secret string, store *secret.Store) *SecurityContext { + return &SecurityContext{ + secret: secret, + store: store, + } +} + +// IsAuthenticated returns true if the secret is valid +func (s *SecurityContext) IsAuthenticated() bool { + if s.store == nil { + return false + } + return s.store.IsValid(s.secret) +} + +// GetUsername returns the corresponding username of the secret +// or null if the secret is invalid +func (s *SecurityContext) GetUsername() string { + if s.store == nil { + return "" + } + return s.store.GetUsername(s.secret) +} + +// IsSysAdmin always returns false +func (s *SecurityContext) IsSysAdmin() bool { + return false +} + +// HasReadPerm returns true if the corresponding user of the secret +// is jobservice, otherwise returns false +func (s *SecurityContext) HasReadPerm(projectIDOrName interface{}) bool { + if s.store == nil { + return false + } + return s.store.GetUsername(s.secret) == secret.JobserviceUser +} + +// HasWritePerm always returns false +func (s *SecurityContext) HasWritePerm(projectIDOrName interface{}) bool { + return false +} + +// HasAllPerm always returns false +func (s *SecurityContext) HasAllPerm(projectIDOrName interface{}) bool { + return false +} diff --git a/src/common/security/secret/context_test.go b/src/common/security/secret/context_test.go new file mode 100644 index 000000000..45db03182 --- /dev/null +++ b/src/common/security/secret/context_test.go @@ -0,0 +1,134 @@ +// 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 secret + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/vmware/harbor/src/common/secret" +) + +func TestIsAuthenticated(t *testing.T) { + // secret store is null + context := NewSecurityContext("", nil) + isAuthenticated := context.IsAuthenticated() + assert.False(t, isAuthenticated) + + //invalid secret + context = NewSecurityContext("invalid_secret", + secret.NewStore(map[string]string{ + "secret": "username", + })) + isAuthenticated = context.IsAuthenticated() + assert.False(t, isAuthenticated) + + //valid secret + context = NewSecurityContext("secret", + secret.NewStore(map[string]string{ + "secret": "username", + })) + isAuthenticated = context.IsAuthenticated() + assert.True(t, isAuthenticated) +} + +func TestGetUsername(t *testing.T) { + // secret store is null + context := NewSecurityContext("", nil) + username := context.GetUsername() + assert.Equal(t, "", username) + + //invalid secret + context = NewSecurityContext("invalid_secret", + secret.NewStore(map[string]string{ + "secret": "username", + })) + username = context.GetUsername() + assert.Equal(t, "", username) + + //valid secret + context = NewSecurityContext("secret", + secret.NewStore(map[string]string{ + "secret": "username", + })) + username = context.GetUsername() + assert.Equal(t, "username", username) +} + +func TestIsSysAdmin(t *testing.T) { + context := NewSecurityContext("secret", + secret.NewStore(map[string]string{ + "secret": "username", + })) + isSysAdmin := context.IsSysAdmin() + assert.False(t, isSysAdmin) +} + +func TestHasReadPerm(t *testing.T) { + // secret store is null + context := NewSecurityContext("", nil) + hasReadPerm := context.HasReadPerm("project_name") + assert.False(t, hasReadPerm) + + //invalid secret + context = NewSecurityContext("invalid_secret", + secret.NewStore(map[string]string{ + "jobservice_secret": secret.JobserviceUser, + })) + hasReadPerm = context.HasReadPerm("project_name") + assert.False(t, hasReadPerm) + + //valid secret, project name + context = NewSecurityContext("jobservice_secret", + secret.NewStore(map[string]string{ + "jobservice_secret": secret.JobserviceUser, + })) + hasReadPerm = context.HasReadPerm("project_name") + assert.True(t, hasReadPerm) + + //valid secret, project ID + hasReadPerm = context.HasReadPerm(1) + assert.True(t, hasReadPerm) +} + +func TestHasWritePerm(t *testing.T) { + context := NewSecurityContext("secret", + secret.NewStore(map[string]string{ + "secret": "username", + })) + + // project name + hasWritePerm := context.HasWritePerm("project_name") + assert.False(t, hasWritePerm) + + // project ID + hasWritePerm = context.HasWritePerm(1) + assert.False(t, hasWritePerm) +} + +func TestHasAllPerm(t *testing.T) { + context := NewSecurityContext("secret", + secret.NewStore(map[string]string{ + "secret": "username", + })) + + // project name + hasAllPerm := context.HasAllPerm("project_name") + assert.False(t, hasAllPerm) + + // project ID + hasAllPerm = context.HasAllPerm(1) + assert.False(t, hasAllPerm) +} diff --git a/src/ui/filter/security.go b/src/ui/filter/security.go index ff36cea76..c24669232 100644 --- a/src/ui/filter/security.go +++ b/src/ui/filter/security.go @@ -19,8 +19,8 @@ import ( "strings" "github.com/astaxie/beego/context" + "github.com/vmware/harbor/src/common/security" "github.com/vmware/harbor/src/common/utils/log" - "github.com/vmware/harbor/src/ui/security" ) const ( diff --git a/src/ui/security/secret/context.go b/src/ui/pms/db/service.go similarity index 97% rename from src/ui/security/secret/context.go rename to src/ui/pms/db/service.go index 6561a2cde..0086c5d31 100644 --- a/src/ui/security/secret/context.go +++ b/src/ui/pms/db/service.go @@ -12,4 +12,4 @@ // See the License for the specific language governing permissions and // limitations under the License. -package secret +package db diff --git a/src/ui/pms/service.go b/src/ui/pms/service.go new file mode 100644 index 000000000..3fc83c9cd --- /dev/null +++ b/src/ui/pms/service.go @@ -0,0 +1,20 @@ +// 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 project + +// PMS is the project mamagement service which abstracts +// the operations related to projects +type PMS interface { +}