@@ -3,6 +3,7 @@ package token
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/dgrijalva/jwt-go"
|
"github.com/dgrijalva/jwt-go"
|
||||||
|
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||||
"kubesphere.io/kubesphere/pkg/server/errors"
|
"kubesphere.io/kubesphere/pkg/server/errors"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
@@ -14,7 +15,7 @@ var errInvalidToken = errors.New("invalid token")
|
|||||||
type claims struct {
|
type claims struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
UID string `json:"uid"`
|
UID string `json:"uid"`
|
||||||
Groups []string `json:"groups"`
|
Email string `json:"email"`
|
||||||
// Currently, we are not using any field in jwt.StandardClaims
|
// Currently, we are not using any field in jwt.StandardClaims
|
||||||
jwt.StandardClaims
|
jwt.StandardClaims
|
||||||
}
|
}
|
||||||
@@ -37,14 +38,14 @@ func (s *jwtTokenIssuer) Verify(tokenString string) (User, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return &AuthUser{Name: clm.Username, UID: clm.UID, Groups: clm.Groups}, nil
|
return &iam.User{Name: clm.Username, UID: clm.UID, Email: clm.Email}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *jwtTokenIssuer) IssueTo(user User) (string, error) {
|
func (s *jwtTokenIssuer) IssueTo(user User) (string, error) {
|
||||||
clm := &claims{
|
clm := &claims{
|
||||||
Username: user.GetName(),
|
Username: user.GetName(),
|
||||||
UID: user.GetUID(),
|
UID: user.GetUID(),
|
||||||
Groups: user.GetGroups(),
|
Email: user.GetEmail(),
|
||||||
StandardClaims: jwt.StandardClaims{
|
StandardClaims: jwt.StandardClaims{
|
||||||
IssuedAt: time.Now().Unix(),
|
IssuedAt: time.Now().Unix(),
|
||||||
Issuer: s.name,
|
Issuer: s.name,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package token
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"kubesphere.io/kubesphere/pkg/api/iam"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -12,19 +13,22 @@ func TestJwtTokenIssuer(t *testing.T) {
|
|||||||
description string
|
description string
|
||||||
name string
|
name string
|
||||||
uid string
|
uid string
|
||||||
|
email string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "admin",
|
name: "admin",
|
||||||
uid: "b8be6edd-2c92-4535-9b2a-df6326474458",
|
uid: "b8be6edd-2c92-4535-9b2a-df6326474458",
|
||||||
|
email: "admin@kubesphere.io",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "bar",
|
name: "bar",
|
||||||
uid: "b8be6edd-2c92-4535-9b2a-df6326474452",
|
uid: "b8be6edd-2c92-4535-9b2a-df6326474452",
|
||||||
|
email: "bar@kubesphere.io",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
user := &AuthUser{
|
user := &iam.User{
|
||||||
Name: testCase.name,
|
Name: testCase.name,
|
||||||
UID: testCase.uid,
|
UID: testCase.uid,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,24 +7,6 @@ type User interface {
|
|||||||
// UID
|
// UID
|
||||||
GetUID() string
|
GetUID() string
|
||||||
|
|
||||||
// Groups
|
// Email
|
||||||
GetGroups() []string
|
GetEmail() string
|
||||||
}
|
|
||||||
|
|
||||||
type AuthUser struct {
|
|
||||||
Name string
|
|
||||||
UID string
|
|
||||||
Groups []string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AuthUser) GetName() string {
|
|
||||||
return a.Name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AuthUser) GetUID() string {
|
|
||||||
return a.UID
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a AuthUser) GetGroups() []string {
|
|
||||||
return a.Groups
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,30 +6,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Username string `json:"username"`
|
Name string `json:"username"`
|
||||||
UID string `json:"uid"`
|
UID string `json:"uid"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Lang string `json:"lang,omitempty"`
|
Lang string `json:"lang,omitempty"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
CreateTime time.Time `json:"create_time"`
|
CreateTime time.Time `json:"createTime"`
|
||||||
Groups []string `json:"groups,omitempty"`
|
Groups []string `json:"groups,omitempty"`
|
||||||
Password string `json:"password,omitempty"`
|
Password string `json:"password,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetName() string {
|
func (u *User) GetName() string {
|
||||||
return u.Username
|
return u.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetUID() string {
|
func (u *User) GetUID() string {
|
||||||
return u.UID
|
return u.UID
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) GetGroups() []string {
|
func (u *User) GetEmail() string {
|
||||||
return u.Groups
|
return u.Email
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *User) Validate() error {
|
func (u *User) Validate() error {
|
||||||
if u.Username == "" {
|
if u.Name == "" {
|
||||||
return errors.New("username can not be empty")
|
return errors.New("username can not be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,20 +29,21 @@ type opaAuthorizer struct {
|
|||||||
am am.AccessManagementInterface
|
am am.AccessManagementInterface
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make decision by request attributes
|
||||||
func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
|
func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized authorizer.Decision, reason string, err error) {
|
||||||
|
|
||||||
|
// Make decisions based on the authorization policy of different levels of roles
|
||||||
platformRole, err := o.am.GetPlatformRole(attr.GetUser().GetName())
|
platformRole, err := o.am.GetPlatformRole(attr.GetUser().GetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return authorizer.DecisionDeny, "", err
|
return authorizer.DecisionDeny, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
// check platform role policy rules
|
// check platform role policy rules
|
||||||
if a, r, e := makeDecision(platformRole, attr); a == authorizer.DecisionAllow {
|
if authorized, reason, err = makeDecision(platformRole, attr); authorized == authorizer.DecisionAllow {
|
||||||
return a, r, e
|
return authorized, reason, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// it's not in cluster resource, permission denied
|
// it's not in cluster resource, permission denied
|
||||||
// TODO declare implicit cluster info in request Info
|
|
||||||
if attr.GetCluster() == "" {
|
if attr.GetCluster() == "" {
|
||||||
return authorizer.DecisionDeny, "permission undefined", nil
|
return authorizer.DecisionDeny, "permission undefined", nil
|
||||||
}
|
}
|
||||||
@@ -78,7 +79,7 @@ func (o *opaAuthorizer) Authorize(attr authorizer.Attributes) (authorized author
|
|||||||
}
|
}
|
||||||
|
|
||||||
if attr.GetNamespace() != "" {
|
if attr.GetNamespace() != "" {
|
||||||
namespaceRole, err := o.am.GetNamespaceRole(attr.GetNamespace(), attr.GetUser().GetName())
|
namespaceRole, err := o.am.GetNamespaceRole(attr.GetCluster(), attr.GetNamespace(), attr.GetUser().GetName())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return authorizer.DecisionDeny, "", err
|
return authorizer.DecisionDeny, "", err
|
||||||
}
|
}
|
||||||
@@ -102,6 +103,29 @@ func makeDecision(role am.Role, a authorizer.Attributes) (authorized authorizer.
|
|||||||
return authorizer.DecisionDeny, "", err
|
return authorizer.DecisionDeny, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// data example
|
||||||
|
//{
|
||||||
|
// "User": {
|
||||||
|
// "Name": "admin",
|
||||||
|
// "UID": "0",
|
||||||
|
// "Groups": [
|
||||||
|
// "admin"
|
||||||
|
// ],
|
||||||
|
// "Extra": null
|
||||||
|
// },
|
||||||
|
// "Verb": "list",
|
||||||
|
// "Cluster": "cluster1",
|
||||||
|
// "Workspace": "",
|
||||||
|
// "Namespace": "",
|
||||||
|
// "APIGroup": "",
|
||||||
|
// "APIVersion": "v1",
|
||||||
|
// "Resource": "nodes",
|
||||||
|
// "Subresource": "",
|
||||||
|
// "Name": "",
|
||||||
|
// "KubernetesRequest": true,
|
||||||
|
// "ResourceRequest": true,
|
||||||
|
// "Path": "/api/v1/nodes"
|
||||||
|
//}
|
||||||
// The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly.
|
// The policy decision is contained in the results returned by the Eval() call. You can inspect the decision and handle it accordingly.
|
||||||
results, err := query.Eval(context.Background(), rego.EvalInput(a))
|
results, err := query.Eval(context.Background(), rego.EvalInput(a))
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,29 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestPlatformRole(t *testing.T) {
|
func TestPlatformRole(t *testing.T) {
|
||||||
|
platformRoles := map[string]am.FakeRole{"admin": {
|
||||||
|
Name: "admin",
|
||||||
|
Rego: "package authz\ndefault allow = true",
|
||||||
|
}, "anonymous": {
|
||||||
|
Name: "anonymous",
|
||||||
|
Rego: "package authz\ndefault allow = false",
|
||||||
|
}, "tom": {
|
||||||
|
Name: "tom",
|
||||||
|
Rego: `package authz
|
||||||
|
default allow = false
|
||||||
|
allow {
|
||||||
|
resources_in_cluster1
|
||||||
|
}
|
||||||
|
resources_in_cluster1 {
|
||||||
|
input.Cluster == "cluster1"
|
||||||
|
}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
opa := NewOPAAuthorizer(am.NewFakeAMOperator(cache.NewSimpleCache()))
|
operator := am.NewFakeAMOperator(cache.NewSimpleCache())
|
||||||
|
operator.Prepare(platformRoles, nil, nil, nil)
|
||||||
|
|
||||||
|
opa := NewOPAAuthorizer(operator)
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
@@ -36,7 +57,7 @@ func TestPlatformRole(t *testing.T) {
|
|||||||
expectedDecision authorizer.Decision
|
expectedDecision authorizer.Decision
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "list nodes",
|
name: "admin can list nodes",
|
||||||
request: authorizer.AttributesRecord{
|
request: authorizer.AttributesRecord{
|
||||||
User: &user.DefaultInfo{
|
User: &user.DefaultInfo{
|
||||||
Name: "admin",
|
Name: "admin",
|
||||||
@@ -60,7 +81,7 @@ func TestPlatformRole(t *testing.T) {
|
|||||||
expectedDecision: authorizer.DecisionAllow,
|
expectedDecision: authorizer.DecisionAllow,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "list nodes",
|
name: "anonymous can not list nodes",
|
||||||
request: authorizer.AttributesRecord{
|
request: authorizer.AttributesRecord{
|
||||||
User: &user.DefaultInfo{
|
User: &user.DefaultInfo{
|
||||||
Name: user.Anonymous,
|
Name: user.Anonymous,
|
||||||
@@ -82,13 +103,54 @@ func TestPlatformRole(t *testing.T) {
|
|||||||
Path: "/api/v1/nodes",
|
Path: "/api/v1/nodes",
|
||||||
},
|
},
|
||||||
expectedDecision: authorizer.DecisionDeny,
|
expectedDecision: authorizer.DecisionDeny,
|
||||||
|
}, {
|
||||||
|
name: "tom can list nodes in cluster1",
|
||||||
|
request: authorizer.AttributesRecord{
|
||||||
|
User: &user.DefaultInfo{
|
||||||
|
Name: "tom",
|
||||||
|
},
|
||||||
|
Verb: "list",
|
||||||
|
Cluster: "cluster1",
|
||||||
|
Workspace: "",
|
||||||
|
Namespace: "",
|
||||||
|
APIGroup: "",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Resource: "nodes",
|
||||||
|
Subresource: "",
|
||||||
|
Name: "",
|
||||||
|
KubernetesRequest: true,
|
||||||
|
ResourceRequest: true,
|
||||||
|
Path: "/api/v1/clusters/cluster1/nodes",
|
||||||
|
},
|
||||||
|
expectedDecision: authorizer.DecisionAllow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "tom can not list nodes in cluster2",
|
||||||
|
request: authorizer.AttributesRecord{
|
||||||
|
User: &user.DefaultInfo{
|
||||||
|
Name: "tom",
|
||||||
|
},
|
||||||
|
Verb: "list",
|
||||||
|
Cluster: "cluster2",
|
||||||
|
Workspace: "",
|
||||||
|
Namespace: "",
|
||||||
|
APIGroup: "",
|
||||||
|
APIVersion: "v1",
|
||||||
|
Resource: "nodes",
|
||||||
|
Subresource: "",
|
||||||
|
Name: "",
|
||||||
|
KubernetesRequest: true,
|
||||||
|
ResourceRequest: true,
|
||||||
|
Path: "/api/v1/clusters/cluster2/nodes",
|
||||||
|
},
|
||||||
|
expectedDecision: authorizer.DecisionDeny,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
decision, _, err := opa.Authorize(test.request)
|
decision, _, err := opa.Authorize(test.request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Error(err)
|
t.Errorf("test failed: %s, %v", test.name, err)
|
||||||
}
|
}
|
||||||
if decision != test.expectedDecision {
|
if decision != test.expectedDecision {
|
||||||
t.Errorf("%s: expected decision %v, actual %+v", test.name, test.expectedDecision, decision)
|
t.Errorf("%s: expected decision %v, actual %+v", test.name, test.expectedDecision, decision)
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ func (h *oauthHandler) TokenReviewHandler(req *restful.Request, resp *restful.Re
|
|||||||
Kind: auth.KindTokenReview,
|
Kind: auth.KindTokenReview,
|
||||||
Status: &auth.Status{
|
Status: &auth.Status{
|
||||||
Authenticated: true,
|
Authenticated: true,
|
||||||
User: map[string]interface{}{"username": user.GetName(), "uid": user.GetUID(), "groups": user.GetGroups()},
|
User: map[string]interface{}{"username": user.GetName(), "uid": user.GetUID()},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ type AccessManagementInterface interface {
|
|||||||
GetPlatformRole(username string) (Role, error)
|
GetPlatformRole(username string) (Role, error)
|
||||||
GetClusterRole(cluster, username string) (Role, error)
|
GetClusterRole(cluster, username string) (Role, error)
|
||||||
GetWorkspaceRole(workspace, username string) (Role, error)
|
GetWorkspaceRole(workspace, username string) (Role, error)
|
||||||
GetNamespaceRole(namespace, username string) (Role, error)
|
GetNamespaceRole(cluster, namespace, username string) (Role, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Role interface {
|
type Role interface {
|
||||||
@@ -73,10 +73,6 @@ func (am *amOperator) GetWorkspaceRole(workspace, username string) (Role, error)
|
|||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (am *amOperator) GetNamespaceRole(namespace, username string) (Role, error) {
|
func (am *amOperator) GetNamespaceRole(cluster, namespace, username string) (Role, error) {
|
||||||
panic("implement me")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (am *amOperator) GetDevOpsRole(namespace, username string) (Role, error) {
|
|
||||||
panic("implement me")
|
panic("implement me")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,55 +19,111 @@
|
|||||||
package am
|
package am
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"k8s.io/apiserver/pkg/authentication/user"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
"kubesphere.io/kubesphere/pkg/simple/client/cache"
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeRole struct {
|
type FakeRole struct {
|
||||||
Name string
|
Name string
|
||||||
Rego string
|
Rego string
|
||||||
}
|
}
|
||||||
type fakeOperator struct {
|
type FakeOperator struct {
|
||||||
cache cache.Interface
|
cache cache.Interface
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFakeRole(username string) Role {
|
func (f FakeOperator) queryFakeRole(cacheKey string) (Role, error) {
|
||||||
if username == user.Anonymous {
|
data, err := f.cache.Get(cacheKey)
|
||||||
return &fakeRole{
|
if err != nil {
|
||||||
Name: "anonymous",
|
if err == cache.ErrNoSuchKey {
|
||||||
|
return &FakeRole{
|
||||||
|
Name: "DenyAll",
|
||||||
Rego: "package authz\ndefault allow = false",
|
Rego: "package authz\ndefault allow = false",
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
return &fakeRole{
|
var role FakeRole
|
||||||
Name: "admin",
|
err = json.Unmarshal([]byte(data), &role)
|
||||||
Rego: "package authz\ndefault allow = true",
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return role, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeOperator) saveFakeRole(cacheKey string, role FakeRole) error {
|
||||||
|
data, err := json.Marshal(role)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return f.cache.Set(cacheKey, string(data), 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeOperator) GetPlatformRole(username string) (Role, error) {
|
||||||
|
return f.queryFakeRole(platformRoleCacheKey(username))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeOperator) GetClusterRole(cluster, username string) (Role, error) {
|
||||||
|
return f.queryFakeRole(clusterRoleCacheKey(cluster, username))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeOperator) GetWorkspaceRole(workspace, username string) (Role, error) {
|
||||||
|
return f.queryFakeRole(workspaceRoleCacheKey(workspace, username))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeOperator) GetNamespaceRole(cluster, namespace, username string) (Role, error) {
|
||||||
|
return f.queryFakeRole(namespaceRoleCacheKey(cluster, namespace, username))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeOperator) Prepare(platformRoles map[string]FakeRole, clusterRoles map[string]map[string]FakeRole, workspaceRoles map[string]map[string]FakeRole, namespaceRoles map[string]map[string]map[string]FakeRole) {
|
||||||
|
|
||||||
|
for username, role := range platformRoles {
|
||||||
|
f.saveFakeRole(platformRoleCacheKey(username), role)
|
||||||
|
}
|
||||||
|
for cluster, roles := range clusterRoles {
|
||||||
|
for username, role := range roles {
|
||||||
|
f.saveFakeRole(clusterRoleCacheKey(cluster, username), role)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f fakeOperator) GetPlatformRole(username string) (Role, error) {
|
for workspace, roles := range workspaceRoles {
|
||||||
return newFakeRole(username), nil
|
for username, role := range roles {
|
||||||
|
f.saveFakeRole(workspaceRoleCacheKey(workspace, username), role)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f fakeOperator) GetClusterRole(cluster, username string) (Role, error) {
|
for cluster, nsRoles := range namespaceRoles {
|
||||||
return newFakeRole(username), nil
|
for namespace, roles := range nsRoles {
|
||||||
|
for username, role := range roles {
|
||||||
|
f.saveFakeRole(namespaceRoleCacheKey(cluster, namespace, username), role)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f fakeOperator) GetWorkspaceRole(workspace, username string) (Role, error) {
|
func namespaceRoleCacheKey(cluster, namespace, username string) string {
|
||||||
return newFakeRole(username), nil
|
return fmt.Sprintf("cluster.%s.namespaces.%s.roles.%s", cluster, namespace, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f fakeOperator) GetNamespaceRole(namespace, username string) (Role, error) {
|
func clusterRoleCacheKey(cluster, username string) string {
|
||||||
return newFakeRole(username), nil
|
return fmt.Sprintf("cluster.%s.roles.%s", cluster, username)
|
||||||
|
}
|
||||||
|
func workspaceRoleCacheKey(workspace, username string) string {
|
||||||
|
return fmt.Sprintf("workspace.%s.roles.%s", workspace, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f fakeRole) GetName() string {
|
func platformRoleCacheKey(username string) string {
|
||||||
|
return fmt.Sprintf("platform.roles.%s", username)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FakeRole) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f fakeRole) GetRego() string {
|
func (f FakeRole) GetRego() string {
|
||||||
return f.Rego
|
return f.Rego
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFakeAMOperator(cache cache.Interface) AccessManagementInterface {
|
func NewFakeAMOperator(cache cache.Interface) *FakeOperator {
|
||||||
return &fakeOperator{cache: cache}
|
return &FakeOperator{cache: cache}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,13 +75,13 @@ func (im *imOperator) ModifyUser(user *iam.User) (*iam.User, error) {
|
|||||||
|
|
||||||
// clear auth failed record
|
// clear auth failed record
|
||||||
if user.Password != "" {
|
if user.Password != "" {
|
||||||
records, err := im.cacheClient.Keys(authenticationFailedKeyForUsername(user.Username, "*"))
|
records, err := im.cacheClient.Keys(authenticationFailedKeyForUsername(user.Name, "*"))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
im.cacheClient.Del(records...)
|
im.cacheClient.Del(records...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return im.ldapClient.Get(user.Username)
|
return im.ldapClient.Get(user.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error) {
|
func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error) {
|
||||||
@@ -100,7 +100,7 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = im.ldapClient.Verify(user.Username, password)
|
err = im.ldapClient.Verify(user.Name, password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == ldap.ErrInvalidCredentials {
|
if err == ldap.ErrInvalidCredentials {
|
||||||
im.cacheClient.Set(authenticationFailedKeyForUsername(username, fmt.Sprintf("%d", time.Now().UnixNano())), "", 30*time.Minute)
|
im.cacheClient.Set(authenticationFailedKeyForUsername(username, fmt.Sprintf("%d", time.Now().UnixNano())), "", 30*time.Minute)
|
||||||
@@ -114,7 +114,7 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: I think we should come up with a better strategy to prevent multiple login.
|
// TODO: I think we should come up with a better strategy to prevent multiple login.
|
||||||
tokenKey := tokenKeyForUsername(user.Username, issuedToken)
|
tokenKey := tokenKeyForUsername(user.Name, issuedToken)
|
||||||
if !im.authenticateOptions.MultipleLogin {
|
if !im.authenticateOptions.MultipleLogin {
|
||||||
// multi login not allowed, remove the previous token
|
// multi login not allowed, remove the previous token
|
||||||
sessions, err := im.cacheClient.Keys(tokenKey)
|
sessions, err := im.cacheClient.Keys(tokenKey)
|
||||||
@@ -136,7 +136,7 @@ func (im *imOperator) Login(username, password, ip string) (*oauth2.Token, error
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
im.logLogin(user.Username, ip, time.Now())
|
im.logLogin(user.Name, ip, time.Now())
|
||||||
|
|
||||||
return &oauth2.Token{AccessToken: issuedToken}, nil
|
return &oauth2.Token{AccessToken: issuedToken}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -216,7 +216,7 @@ func (l *ldapInterfaceImpl) Get(name string) (*iam.User, error) {
|
|||||||
userEntry := searchResults.Entries[0]
|
userEntry := searchResults.Entries[0]
|
||||||
|
|
||||||
user := &iam.User{
|
user := &iam.User{
|
||||||
Username: userEntry.GetAttributeValue(ldapAttributeUserID),
|
Name: userEntry.GetAttributeValue(ldapAttributeUserID),
|
||||||
Email: userEntry.GetAttributeValue(ldapAttributeMail),
|
Email: userEntry.GetAttributeValue(ldapAttributeMail),
|
||||||
Lang: userEntry.GetAttributeValue(ldapAttributePreferredLanguage),
|
Lang: userEntry.GetAttributeValue(ldapAttributePreferredLanguage),
|
||||||
Description: userEntry.GetAttributeValue(ldapAttributeDescription),
|
Description: userEntry.GetAttributeValue(ldapAttributeDescription),
|
||||||
@@ -229,12 +229,12 @@ func (l *ldapInterfaceImpl) Get(name string) (*iam.User, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *ldapInterfaceImpl) Create(user *iam.User) error {
|
func (l *ldapInterfaceImpl) Create(user *iam.User) error {
|
||||||
if _, err := l.Get(user.Username); err != nil {
|
if _, err := l.Get(user.Name); err != nil {
|
||||||
return ErrUserAlreadyExisted
|
return ErrUserAlreadyExisted
|
||||||
}
|
}
|
||||||
|
|
||||||
createRequest := &ldap.AddRequest{
|
createRequest := &ldap.AddRequest{
|
||||||
DN: l.dnForUsername(user.Username),
|
DN: l.dnForUsername(user.Name),
|
||||||
Attributes: []ldap.Attribute{
|
Attributes: []ldap.Attribute{
|
||||||
{
|
{
|
||||||
Type: ldapAttributeObjectClass,
|
Type: ldapAttributeObjectClass,
|
||||||
@@ -242,7 +242,7 @@ func (l *ldapInterfaceImpl) Create(user *iam.User) error {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: ldapAttributeCommonName,
|
Type: ldapAttributeCommonName,
|
||||||
Vals: []string{user.Username},
|
Vals: []string{user.Name},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: ldapAttributeSerialNumber,
|
Type: ldapAttributeSerialNumber,
|
||||||
@@ -254,11 +254,11 @@ func (l *ldapInterfaceImpl) Create(user *iam.User) error {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: ldapAttributeHomeDirectory,
|
Type: ldapAttributeHomeDirectory,
|
||||||
Vals: []string{"/home/" + user.Username},
|
Vals: []string{"/home/" + user.Name},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: ldapAttributeUserID,
|
Type: ldapAttributeUserID,
|
||||||
Vals: []string{user.Username},
|
Vals: []string{user.Name},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: ldapAttributeUserIDNumber,
|
Type: ldapAttributeUserIDNumber,
|
||||||
@@ -322,13 +322,13 @@ func (l *ldapInterfaceImpl) Update(newUser *iam.User) error {
|
|||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
// check user existed
|
// check user existed
|
||||||
_, err = l.Get(newUser.Username)
|
_, err = l.Get(newUser.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
modifyRequest := &ldap.ModifyRequest{
|
modifyRequest := &ldap.ModifyRequest{
|
||||||
DN: l.dnForUsername(newUser.Username),
|
DN: l.dnForUsername(newUser.Name),
|
||||||
}
|
}
|
||||||
|
|
||||||
if newUser.Description != "" {
|
if newUser.Description != "" {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ func NewSimpleLdap() Interface {
|
|||||||
|
|
||||||
// initialize with a admin user
|
// initialize with a admin user
|
||||||
admin := &iam.User{
|
admin := &iam.User{
|
||||||
Username: "admin",
|
Name: "admin",
|
||||||
Email: "admin@kubesphere.io",
|
Email: "admin@kubesphere.io",
|
||||||
Lang: "eng",
|
Lang: "eng",
|
||||||
Description: "administrator",
|
Description: "administrator",
|
||||||
@@ -25,21 +25,21 @@ func NewSimpleLdap() Interface {
|
|||||||
Groups: nil,
|
Groups: nil,
|
||||||
Password: "P@88w0rd",
|
Password: "P@88w0rd",
|
||||||
}
|
}
|
||||||
sl.store[admin.Username] = admin
|
sl.store[admin.Name] = admin
|
||||||
return sl
|
return sl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s simpleLdap) Create(user *iam.User) error {
|
func (s simpleLdap) Create(user *iam.User) error {
|
||||||
s.store[user.Username] = user
|
s.store[user.Name] = user
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s simpleLdap) Update(user *iam.User) error {
|
func (s simpleLdap) Update(user *iam.User) error {
|
||||||
_, err := s.Get(user.Username)
|
_, err := s.Get(user.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
s.store[user.Username] = user
|
s.store[user.Name] = user
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ func TestSimpleLdap(t *testing.T) {
|
|||||||
ldapClient := NewSimpleLdap()
|
ldapClient := NewSimpleLdap()
|
||||||
|
|
||||||
foo := &iam.User{
|
foo := &iam.User{
|
||||||
Username: "jerry",
|
Name: "jerry",
|
||||||
Email: "jerry@kubesphere.io",
|
Email: "jerry@kubesphere.io",
|
||||||
Lang: "en",
|
Lang: "en",
|
||||||
Description: "Jerry is kind and gentle.",
|
Description: "Jerry is kind and gentle.",
|
||||||
@@ -27,7 +27,7 @@ func TestSimpleLdap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if user really created
|
// check if user really created
|
||||||
user, err := ldapClient.Get(foo.Username)
|
user, err := ldapClient.Get(foo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ func TestSimpleLdap(t *testing.T) {
|
|||||||
t.Fatalf("%T differ (-got, +want): %s", user, diff)
|
t.Fatalf("%T differ (-got, +want): %s", user, diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = ldapClient.Delete(foo.Username)
|
_ = ldapClient.Delete(foo.Name)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should update user", func(t *testing.T) {
|
t.Run("should update user", func(t *testing.T) {
|
||||||
@@ -51,7 +51,7 @@ func TestSimpleLdap(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if user really created
|
// check if user really created
|
||||||
user, err := ldapClient.Get(foo.Username)
|
user, err := ldapClient.Get(foo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@@ -59,7 +59,7 @@ func TestSimpleLdap(t *testing.T) {
|
|||||||
t.Fatalf("%T differ (-got, +want): %s", user, diff)
|
t.Fatalf("%T differ (-got, +want): %s", user, diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = ldapClient.Delete(foo.Username)
|
_ = ldapClient.Delete(foo.Name)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("should delete user", func(t *testing.T) {
|
t.Run("should delete user", func(t *testing.T) {
|
||||||
@@ -68,12 +68,12 @@ func TestSimpleLdap(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ldapClient.Delete(foo.Username)
|
err = ldapClient.Delete(foo.Name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = ldapClient.Get(foo.Username)
|
_, err = ldapClient.Get(foo.Name)
|
||||||
if err == nil || err != ErrUserNotExists {
|
if err == nil || err != ErrUserNotExists {
|
||||||
t.Fatalf("expected ErrUserNotExists error, got %v", err)
|
t.Fatalf("expected ErrUserNotExists error, got %v", err)
|
||||||
}
|
}
|
||||||
@@ -85,12 +85,12 @@ func TestSimpleLdap(t *testing.T) {
|
|||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ldapClient.Verify(foo.Username, foo.Password)
|
err = ldapClient.Verify(foo.Name, foo.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("should pass but got an error %v", err)
|
t.Fatalf("should pass but got an error %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ldapClient.Verify(foo.Username, "gibberish")
|
err = ldapClient.Verify(foo.Name, "gibberish")
|
||||||
if err == nil || err != ErrInvalidCredentials {
|
if err == nil || err != ErrInvalidCredentials {
|
||||||
t.Fatalf("expected error ErrInvalidCrenentials but got %v", err)
|
t.Fatalf("expected error ErrInvalidCrenentials but got %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user