mirror of
https://github.com/goharbor/harbor
synced 2025-05-17 20:02:51 +00:00
update codes of auth context
This commit is contained in:
parent
2818c047bf
commit
3ebe1a824b
@ -23,6 +23,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO update the value of role when admiral API is ready
|
||||||
const (
|
const (
|
||||||
// AuthTokenHeader is the key of auth token header
|
// AuthTokenHeader is the key of auth token header
|
||||||
AuthTokenHeader = "x-xenon-auth-token"
|
AuthTokenHeader = "x-xenon-auth-token"
|
||||||
@ -32,16 +33,19 @@ const (
|
|||||||
guestRole = "GUEST"
|
guestRole = "GUEST"
|
||||||
)
|
)
|
||||||
|
|
||||||
var client = &http.Client{
|
type project struct {
|
||||||
Transport: &http.Transport{},
|
DocumentSelfLink string `json:"documentSelfLink"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Roles []string `json:"roles"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AuthContext ...
|
// AuthContext ...
|
||||||
type AuthContext struct {
|
type AuthContext struct {
|
||||||
PrincipalID string `json:"principalId"`
|
PrincipalID string `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Roles []string `json:"projects"`
|
Email string `json:"email"`
|
||||||
Projects map[string][]string `json:"roles"`
|
Roles []string `json:"roles"`
|
||||||
|
Projects []*project `json:"projects"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsername ...
|
// GetUsername ...
|
||||||
@ -53,7 +57,6 @@ func (a *AuthContext) GetUsername() string {
|
|||||||
func (a *AuthContext) IsSysAdmin() bool {
|
func (a *AuthContext) IsSysAdmin() bool {
|
||||||
isSysAdmin := false
|
isSysAdmin := false
|
||||||
for _, role := range a.Roles {
|
for _, role := range a.Roles {
|
||||||
// TODO update the value of role when admiral API is ready
|
|
||||||
if role == sysAdminRole {
|
if role == sysAdminRole {
|
||||||
isSysAdmin = true
|
isSysAdmin = true
|
||||||
break
|
break
|
||||||
@ -63,14 +66,14 @@ func (a *AuthContext) IsSysAdmin() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HasReadPerm ...
|
// HasReadPerm ...
|
||||||
func (a *AuthContext) HasReadPerm(project string) bool {
|
func (a *AuthContext) HasReadPerm(projectName string) bool {
|
||||||
_, exist := a.Projects[project]
|
roles := a.getRoles(projectName)
|
||||||
return exist
|
return len(roles) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasWritePerm ...
|
// HasWritePerm ...
|
||||||
func (a *AuthContext) HasWritePerm(project string) bool {
|
func (a *AuthContext) HasWritePerm(projectName string) bool {
|
||||||
roles, _ := a.Projects[project]
|
roles := a.getRoles(projectName)
|
||||||
for _, role := range roles {
|
for _, role := range roles {
|
||||||
if role == projectAdminRole || role == developerRole {
|
if role == projectAdminRole || role == developerRole {
|
||||||
return true
|
return true
|
||||||
@ -80,8 +83,8 @@ func (a *AuthContext) HasWritePerm(project string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// HasAllPerm ...
|
// HasAllPerm ...
|
||||||
func (a *AuthContext) HasAllPerm(project string) bool {
|
func (a *AuthContext) HasAllPerm(projectName string) bool {
|
||||||
roles, _ := a.Projects[project]
|
roles := a.getRoles(projectName)
|
||||||
for _, role := range roles {
|
for _, role := range roles {
|
||||||
if role == projectAdminRole {
|
if role == projectAdminRole {
|
||||||
return true
|
return true
|
||||||
@ -90,66 +93,57 @@ func (a *AuthContext) HasAllPerm(project string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMyProjects returns all projects which the user is a member of
|
func (a *AuthContext) getRoles(projectName string) []string {
|
||||||
func (a *AuthContext) GetMyProjects() ([]string, error) {
|
for _, project := range a.Projects {
|
||||||
existence := map[string]bool{}
|
if project.Name == projectName {
|
||||||
projects := []string{}
|
return project.Roles
|
||||||
for _, list := range a.Projects {
|
|
||||||
for _, p := range list {
|
|
||||||
if existence[p] {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
existence[p] = true
|
|
||||||
projects = append(projects, p)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return projects, nil
|
|
||||||
|
return []string{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetByToken ...
|
// GetMyProjects returns all projects which the user is a member of
|
||||||
func GetByToken(url, token string) (*AuthContext, error) {
|
func (a *AuthContext) GetMyProjects() []string {
|
||||||
return get(url, token)
|
projects := []string{}
|
||||||
|
for _, project := range a.Projects {
|
||||||
|
projects = append(projects, project.Name)
|
||||||
|
}
|
||||||
|
return projects
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAuthCtxOfUser gets the user's auth context
|
// GetAuthCtxOfCurrentUser returns the auth context of the current user
|
||||||
func GetAuthCtxOfUser(url, token string, username string) (*AuthContext, error) {
|
func GetAuthCtxOfCurrentUser(client *http.Client, url, token string) (*AuthContext, error) {
|
||||||
return get(url, token, username)
|
return get(client, url, token)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAuthCtxOfSpecificUser returns the auth context of the specific user
|
||||||
|
func GetAuthCtxOfSpecificUser(client *http.Client, url, token string, username string) (*AuthContext, error) {
|
||||||
|
return get(client, url, token, username)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the user's auth context, if the username is not provided
|
// get the user's auth context, if the username is not provided
|
||||||
// get the default auth context of the token
|
// get the default auth context of the token
|
||||||
func get(url, token string, username ...string) (*AuthContext, error) {
|
func get(client *http.Client, url, token string, username ...string) (*AuthContext, error) {
|
||||||
principalID := ""
|
endpoint := ""
|
||||||
if len(username) > 0 {
|
if len(username) > 0 && len(username[0]) > 0 {
|
||||||
principalID = username[0]
|
endpoint = buildSpecificUserAuthCtxURL(url, username[0])
|
||||||
|
} else {
|
||||||
|
endpoint = buildCurrentUserAuthCtxURL(url)
|
||||||
}
|
}
|
||||||
req, err := http.NewRequest(http.MethodGet, buildCtxURL(url, principalID), nil)
|
|
||||||
|
req, err := http.NewRequest(http.MethodGet, endpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Header.Add(AuthTokenHeader, token)
|
req.Header.Add(AuthTokenHeader, token)
|
||||||
|
|
||||||
code, _, data, err := send(req)
|
return send(client, req)
|
||||||
|
|
||||||
if code != http.StatusOK {
|
|
||||||
return nil, fmt.Errorf("failed to get auth context by token: %d %s",
|
|
||||||
code, string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := &AuthContext{
|
|
||||||
Projects: make(map[string][]string),
|
|
||||||
}
|
|
||||||
if err = json.Unmarshal(data, ctx); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login with credential and returns token, auth context and error
|
// Login with credential and returns auth context and error
|
||||||
func Login(url, username, password string) (string, *AuthContext, error) {
|
func Login(client *http.Client, url, username, password string) (*AuthContext, error) {
|
||||||
data, err := json.Marshal(&struct {
|
data, err := json.Marshal(&struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
@ -158,52 +152,51 @@ func Login(url, username, password string) (string, *AuthContext, error) {
|
|||||||
Password: password,
|
Password: password,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest(http.MethodPost, buildLoginURL(url), bytes.NewBuffer(data))
|
req, err := http.NewRequest(http.MethodPost, buildLoginURL(url), bytes.NewBuffer(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
code, header, data, err := send(req)
|
return send(client, req)
|
||||||
if code != http.StatusOK {
|
|
||||||
return "", nil, fmt.Errorf("failed to login with user %s: %d %s", username,
|
|
||||||
code, string(data))
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := &AuthContext{
|
|
||||||
Projects: make(map[string][]string),
|
|
||||||
}
|
|
||||||
if err = json.Unmarshal(data, ctx); err != nil {
|
|
||||||
return "", nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return header.Get(AuthTokenHeader), ctx, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func send(req *http.Request) (int, http.Header, []byte, error) {
|
func send(client *http.Client, req *http.Request) (*AuthContext, error) {
|
||||||
resp, err := client.Do(req)
|
resp, err := client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
|
|
||||||
data, err := ioutil.ReadAll(resp.Body)
|
data, err := ioutil.ReadAll(resp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return resp.StatusCode, resp.Header, data, nil
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
return nil, fmt.Errorf("unexpected status code: %d %s", resp.StatusCode, string(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := &AuthContext{}
|
||||||
|
if err = json.Unmarshal(data, ctx); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildCtxURL(url, principalID string) string {
|
func buildCurrentUserAuthCtxURL(url string) string {
|
||||||
url = strings.TrimRight(url, "/") + "/sso/auth-context"
|
return strings.TrimRight(url, "/") + "/auth/session"
|
||||||
if len(principalID) > 0 {
|
|
||||||
url += "/" + principalID
|
|
||||||
}
|
|
||||||
return url
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildSpecificUserAuthCtxURL(url, principalID string) string {
|
||||||
|
return fmt.Sprintf("%s/auth/idm/principals/%s/security-context",
|
||||||
|
strings.TrimRight(url, "/"), principalID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO update the url
|
||||||
func buildLoginURL(url string) string {
|
func buildLoginURL(url string) string {
|
||||||
return strings.TrimRight(url, "/") + "/sso/login"
|
return strings.TrimRight(url, "/") + "/sso/login"
|
||||||
}
|
}
|
||||||
|
@ -79,12 +79,18 @@ func (p *ProjectAPI) Post() {
|
|||||||
p.HandleUnauthorized()
|
p.HandleUnauthorized()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
var onlyAdmin bool
|
||||||
onlyAdmin, err := config.OnlyAdminCreateProject()
|
var err error
|
||||||
if err != nil {
|
if config.WithAdmiral() {
|
||||||
log.Errorf("failed to determine whether only admin can create projects: %v", err)
|
onlyAdmin = true
|
||||||
p.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
} else {
|
||||||
|
onlyAdmin, err = config.OnlyAdminCreateProject()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to determine whether only admin can create projects: %v", err)
|
||||||
|
p.CustomAbort(http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if onlyAdmin && !p.SecurityCtx.IsSysAdmin() {
|
if onlyAdmin && !p.SecurityCtx.IsSysAdmin() {
|
||||||
log.Errorf("Only sys admin can create project")
|
log.Errorf("Only sys admin can create project")
|
||||||
p.RenderError(http.StatusForbidden, "Only system admin can create project")
|
p.RenderError(http.StatusForbidden, "Only system admin can create project")
|
||||||
|
@ -113,14 +113,14 @@ func initProjectManager() {
|
|||||||
// integration with admiral
|
// integration with admiral
|
||||||
log.Info("initializing the project manager based on PMS...")
|
log.Info("initializing the project manager based on PMS...")
|
||||||
// TODO read ca/cert file and pass it to the TLS config
|
// TODO read ca/cert file and pass it to the TLS config
|
||||||
AdminserverClient := &http.Client{
|
AdmiralClient = &http.Client{
|
||||||
Transport: &http.Transport{
|
Transport: &http.Transport{
|
||||||
TLSClientConfig: &tls.Config{
|
TLSClientConfig: &tls.Config{
|
||||||
InsecureSkipVerify: true,
|
InsecureSkipVerify: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
GlobalProjectMgr = pms.NewProjectManager(AdminserverClient,
|
GlobalProjectMgr = pms.NewProjectManager(AdmiralClient,
|
||||||
AdmiralEndpoint(), &pms.FileTokenReader{
|
AdmiralEndpoint(), &pms.FileTokenReader{
|
||||||
Path: defaultTokenFilePath,
|
Path: defaultTokenFilePath,
|
||||||
})
|
})
|
||||||
|
@ -131,7 +131,8 @@ func (b *basicAuthReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
|||||||
|
|
||||||
if config.WithAdmiral() {
|
if config.WithAdmiral() {
|
||||||
// integration with admiral
|
// integration with admiral
|
||||||
_, authCtx, err := authcontext.Login(config.AdmiralEndpoint(), username, password)
|
authCtx, err := authcontext.Login(config.AdmiralClient,
|
||||||
|
config.AdmiralEndpoint(), username, 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
|
return false
|
||||||
@ -203,7 +204,8 @@ func (t *tokenReqCtxModifier) Modify(ctx *beegoctx.Context) bool {
|
|||||||
|
|
||||||
log.Debug("got token from request")
|
log.Debug("got token from request")
|
||||||
|
|
||||||
authContext, err := authcontext.GetByToken(config.AdmiralEndpoint(), token)
|
authContext, err := authcontext.GetAuthCtxOfCurrentUser(config.AdmiralClient,
|
||||||
|
config.AdmiralEndpoint(), token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("failed to get auth context: %v", err)
|
log.Errorf("failed to get auth context: %v", err)
|
||||||
return false
|
return false
|
||||||
|
@ -304,16 +304,12 @@ func (p *ProjectManager) GetPublic() ([]*models.Project, error) {
|
|||||||
// GetByMember ...
|
// GetByMember ...
|
||||||
func (p *ProjectManager) GetByMember(username string) ([]*models.Project, error) {
|
func (p *ProjectManager) GetByMember(username string) ([]*models.Project, error) {
|
||||||
projects := []*models.Project{}
|
projects := []*models.Project{}
|
||||||
ctx, err := authcontext.GetAuthCtxOfUser(p.endpoint, p.getToken(), username)
|
ctx, err := authcontext.GetAuthCtxOfSpecificUser(p.client, p.endpoint, p.getToken(), username)
|
||||||
if err != nil {
|
|
||||||
return projects, err
|
|
||||||
}
|
|
||||||
|
|
||||||
names, err := ctx.GetMyProjects()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return projects, err
|
return projects, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
names := ctx.GetMyProjects()
|
||||||
for _, name := range names {
|
for _, name := range names {
|
||||||
project, err := p.Get(name)
|
project, err := p.Get(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user