mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-05 15:38:20 +01:00
Merge pull request #7463 from reasonerjt/regen-cli-secret
Provide API to generate CLI secret
This commit is contained in:
commit
5cd3c039a9
@ -940,6 +940,44 @@ paths:
|
|||||||
description: User ID does not exist.
|
description: User ID does not exist.
|
||||||
'500':
|
'500':
|
||||||
description: Unexpected internal errors.
|
description: Unexpected internal errors.
|
||||||
|
'/users/{user_id}/gen_cli_secret':
|
||||||
|
post:
|
||||||
|
summary: Generate new CLI secret for a user.
|
||||||
|
description: |
|
||||||
|
This endpoint let user generate a new CLI secret for himself. This API only works when auth mode is set to 'OIDC'.
|
||||||
|
Once this API returns with successful status, the old secret will be invalid, as there will be only one CLI secret
|
||||||
|
for a user. The new secret will be returned in the response.
|
||||||
|
parameters:
|
||||||
|
- name: user_id
|
||||||
|
in: path
|
||||||
|
type: integer
|
||||||
|
format: int
|
||||||
|
required: true
|
||||||
|
description: User ID
|
||||||
|
tags:
|
||||||
|
- Products
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: The secret is successfully generated.
|
||||||
|
schema:
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
secret:
|
||||||
|
type: string
|
||||||
|
description: The new secret
|
||||||
|
'400':
|
||||||
|
description: Invalid user ID. Or user is not onboarded via OIDC authentication.
|
||||||
|
'401':
|
||||||
|
description: User need to log in first.
|
||||||
|
'403':
|
||||||
|
description: Non-admin user can only generate the cli secret of himself.
|
||||||
|
'404':
|
||||||
|
description: User ID does not exist.
|
||||||
|
'412':
|
||||||
|
description: The auth mode of the system is not "oidc_auth", or the user is not onboarded via OIDC AuthN.
|
||||||
|
'500':
|
||||||
|
description: Unexpected internal errors.
|
||||||
|
|
||||||
/repositories:
|
/repositories:
|
||||||
get:
|
get:
|
||||||
summary: Get repositories accompany with relevant project and repo name.
|
summary: Get repositories accompany with relevant project and repo name.
|
||||||
|
@ -38,7 +38,7 @@ CREATE TABLE oidc_user (
|
|||||||
UNIQUE (subiss)
|
UNIQUE (subiss)
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TRIGGER odic_user_update_time_at_modtime BEFORE UPDATE ON oidc_user FOR EACH ROW EXECUTE PROCEDURE update_update_time_at_column();
|
CREATE TRIGGER oidc_user_update_time_at_modtime BEFORE UPDATE ON oidc_user FOR EACH ROW EXECUTE PROCEDURE update_update_time_at_column();
|
||||||
|
|
||||||
/*add master role*/
|
/*add master role*/
|
||||||
INSERT INTO role (role_code, name) VALUES ('DRWS', 'master');
|
INSERT INTO role (role_code, name) VALUES ('DRWS', 'master');
|
||||||
|
@ -92,10 +92,17 @@ func GetOIDCUserByUserID(userID int) (*models.OIDCUser, error) {
|
|||||||
return &oidcUsers[0], nil
|
return &oidcUsers[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOIDCUser ...
|
// UpdateOIDCUser updates the OIDCUser based on the input parm, only the column "secret" and "token" can be updated
|
||||||
func UpdateOIDCUser(oidcUser *models.OIDCUser) error {
|
func UpdateOIDCUser(oidcUser *models.OIDCUser) error {
|
||||||
oidcUser.UpdateTime = time.Now()
|
cols := []string{"secret", "token"}
|
||||||
_, err := GetOrmer().Update(oidcUser)
|
_, err := GetOrmer().Update(oidcUser, cols...)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateOIDCUserSecret updates the secret of the OIDC User. The secret in the input parm should be encrypted before
|
||||||
|
// calling this func
|
||||||
|
func UpdateOIDCUserSecret(oidcUser *models.OIDCUser) error {
|
||||||
|
_, err := GetOrmer().Update(oidcUser, "secret")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,15 +80,13 @@ func TestOIDCUserMetaDaoMethods(t *testing.T) {
|
|||||||
ID: ou111.ID,
|
ID: ou111.ID,
|
||||||
UserID: ou111.UserID,
|
UserID: ou111.UserID,
|
||||||
SubIss: "newSub",
|
SubIss: "newSub",
|
||||||
|
Secret: "newSecret",
|
||||||
}
|
}
|
||||||
require.Nil(t, UpdateOIDCUser(meta3))
|
require.Nil(t, UpdateOIDCUser(meta3))
|
||||||
oidcUser1Update, err := GetOIDCUserByID(ou111.ID)
|
oidcUser1Update, err := GetOIDCUserByID(ou111.ID)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
assert.Equal(t, "newSub", oidcUser1Update.SubIss)
|
assert.Equal(t, "QWE123123RT1", oidcUser1Update.SubIss)
|
||||||
|
assert.Equal(t, "newSecret", oidcUser1Update.Secret)
|
||||||
user, err := GetUserBySubIss("new", "Sub")
|
|
||||||
require.Nil(t, err)
|
|
||||||
assert.Equal(t, "user111", user.Username)
|
|
||||||
|
|
||||||
// clear data
|
// clear data
|
||||||
defer func() {
|
defer func() {
|
||||||
|
@ -38,6 +38,7 @@ type UserAPI struct {
|
|||||||
SelfRegistration bool
|
SelfRegistration bool
|
||||||
IsAdmin bool
|
IsAdmin bool
|
||||||
AuthMode string
|
AuthMode string
|
||||||
|
secretKey string
|
||||||
}
|
}
|
||||||
|
|
||||||
type passwordReq struct {
|
type passwordReq struct {
|
||||||
@ -50,6 +51,10 @@ type userSearch struct {
|
|||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type secretResp struct {
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
// Prepare validates the URL and parms
|
// Prepare validates the URL and parms
|
||||||
func (ua *UserAPI) Prepare() {
|
func (ua *UserAPI) Prepare() {
|
||||||
ua.BaseController.Prepare()
|
ua.BaseController.Prepare()
|
||||||
@ -61,6 +66,15 @@ func (ua *UserAPI) Prepare() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ua.AuthMode = mode
|
ua.AuthMode = mode
|
||||||
|
if mode == common.OIDCAuth {
|
||||||
|
key, err := config.SecretKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to get secret key: %v", err)
|
||||||
|
ua.SendInternalServerError(fmt.Errorf("failed to get secret key: %v", err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua.secretKey = key
|
||||||
|
}
|
||||||
|
|
||||||
self, err := config.SelfRegistration()
|
self, err := config.SelfRegistration()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -475,17 +489,53 @@ func (ua *UserAPI) ListUserPermissions() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ua *UserAPI) getOIDCUserInfo() (*models.OIDCUser, error) {
|
// GenCLISecret generates a new CLI secret and replace the old one
|
||||||
key, err := config.SecretKey()
|
func (ua *UserAPI) GenCLISecret() {
|
||||||
if err != nil {
|
if ua.AuthMode != common.OIDCAuth {
|
||||||
return nil, err
|
ua.SendPreconditionFailedError(errors.New("the auth mode has to be oidc auth"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
if ua.userID != ua.currentUserID && !ua.IsAdmin {
|
||||||
|
ua.SendForbiddenError(errors.New(""))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oidcData, err := dao.GetOIDCUserByUserID(ua.userID)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to get OIDC User meta for user, id: %d, error: %v", ua.userID, err)
|
||||||
|
ua.SendInternalServerError(errors.New("failed to get OIDC meta data for user"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if oidcData == nil {
|
||||||
|
log.Errorf("User is not onboarded via OIDC AuthN, user id: %d", ua.userID)
|
||||||
|
ua.SendPreconditionFailedError(errors.New("user is not onboarded via OIDC AuthN"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
sec := utils.GenerateRandomString()
|
||||||
|
encSec, err := utils.ReversibleEncrypt(sec, ua.secretKey)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to encrypt secret, error: %v", err)
|
||||||
|
ua.SendInternalServerError(errors.New("failed to encrypt secret"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
oidcData.Secret = encSec
|
||||||
|
err = dao.UpdateOIDCUserSecret(oidcData)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Failed to update secret in DB, error: %v", err)
|
||||||
|
ua.SendInternalServerError(errors.New("failed to update secret in DB"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ua.Data["json"] = secretResp{sec}
|
||||||
|
ua.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ua *UserAPI) getOIDCUserInfo() (*models.OIDCUser, error) {
|
||||||
o, err := dao.GetOIDCUserByUserID(ua.userID)
|
o, err := dao.GetOIDCUserByUserID(ua.userID)
|
||||||
if err != nil || o == nil {
|
if err != nil || o == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if len(o.Secret) > 0 {
|
if len(o.Secret) > 0 {
|
||||||
p, err := utils.ReversibleDecrypt(o.Secret, key)
|
p, err := utils.ReversibleDecrypt(o.Secret, ua.secretKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -53,6 +53,7 @@ func initRouters() {
|
|||||||
beego.Router("/api/users/:id([0-9]+)/password", &api.UserAPI{}, "put:ChangePassword")
|
beego.Router("/api/users/:id([0-9]+)/password", &api.UserAPI{}, "put:ChangePassword")
|
||||||
beego.Router("/api/users/:id/permissions", &api.UserAPI{}, "get:ListUserPermissions")
|
beego.Router("/api/users/:id/permissions", &api.UserAPI{}, "get:ListUserPermissions")
|
||||||
beego.Router("/api/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole")
|
beego.Router("/api/users/:id/sysadmin", &api.UserAPI{}, "put:ToggleUserAdminRole")
|
||||||
|
beego.Router("/api/users/:id/gen_cli_secret", &api.UserAPI{}, "post:GenCLISecret")
|
||||||
beego.Router("/api/usergroups/?:ugid([0-9]+)", &api.UserGroupAPI{})
|
beego.Router("/api/usergroups/?:ugid([0-9]+)", &api.UserGroupAPI{})
|
||||||
beego.Router("/api/ldap/ping", &api.LdapAPI{}, "post:Ping")
|
beego.Router("/api/ldap/ping", &api.LdapAPI{}, "post:Ping")
|
||||||
beego.Router("/api/ldap/users/search", &api.LdapAPI{}, "get:Search")
|
beego.Router("/api/ldap/users/search", &api.LdapAPI{}, "get:Search")
|
||||||
|
Loading…
Reference in New Issue
Block a user