user: add a permission to disable changing api key authentication

also implement the missing APIs to enable/disable api key authentication
This commit is contained in:
Nicola Murino 2021-09-06 18:46:35 +02:00
parent 101c2962ab
commit 7bad65a43e
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
11 changed files with 479 additions and 8 deletions

View file

@ -724,6 +724,11 @@ func (u *User) CanChangePassword() bool {
return !util.IsStringInSlice(sdk.WebClientPasswordChangeDisabled, u.Filters.WebClient)
}
// CanChangeAPIKeyAuth returns true if this user is allowed to enable/disable API key authentication
func (u *User) CanChangeAPIKeyAuth() bool {
return !util.IsStringInSlice(sdk.WebClientAPIKeyAuthChangeDisabled, u.Filters.WebClient)
}
// CanManagePublicKeys returns true if this user is allowed to manage public keys
// from the web client. Used in web client UI
func (u *User) CanManagePublicKeys() bool {

View file

@ -155,6 +155,50 @@ func deleteAdmin(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, err, "Admin deleted", http.StatusOK)
}
func getAdminAPIKeyAuthStatus(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
claims, err := getTokenClaims(r)
if err != nil || claims.Username == "" {
sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
return
}
admin, err := dataprovider.AdminExists(claims.Username)
if err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
return
}
resp := apiKeyAuth{
AllowAPIKeyAuth: admin.Filters.AllowAPIKeyAuth,
}
render.JSON(w, r, resp)
}
func changeAdminAPIKeyAuthStatus(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
claims, err := getTokenClaims(r)
if err != nil || claims.Username == "" {
sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
return
}
admin, err := dataprovider.AdminExists(claims.Username)
if err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
return
}
var req apiKeyAuth
err = render.DecodeJSON(r.Body, &req)
if err != nil {
sendAPIResponse(w, r, err, "", http.StatusBadRequest)
return
}
admin.Filters.AllowAPIKeyAuth = req.AllowAPIKeyAuth
if err := dataprovider.UpdateAdmin(&admin); err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
return
}
sendAPIResponse(w, r, err, "API key authentication status updated", http.StatusOK)
}
func changeAdminPassword(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)

View file

@ -349,6 +349,50 @@ func setUserPublicKeys(w http.ResponseWriter, r *http.Request) {
sendAPIResponse(w, r, err, "Public keys updated", http.StatusOK)
}
func getUserAPIKeyAuthStatus(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
claims, err := getTokenClaims(r)
if err != nil || claims.Username == "" {
sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
return
}
user, err := dataprovider.UserExists(claims.Username)
if err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
return
}
resp := apiKeyAuth{
AllowAPIKeyAuth: user.Filters.AllowAPIKeyAuth,
}
render.JSON(w, r, resp)
}
func changeUserAPIKeyAuthStatus(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
claims, err := getTokenClaims(r)
if err != nil || claims.Username == "" {
sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
return
}
var req apiKeyAuth
err = render.DecodeJSON(r.Body, &req)
if err != nil {
sendAPIResponse(w, r, err, "", http.StatusBadRequest)
return
}
user, err := dataprovider.UserExists(claims.Username)
if err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
return
}
user.Filters.AllowAPIKeyAuth = req.AllowAPIKeyAuth
if err := dataprovider.UpdateUser(&user); err != nil {
sendAPIResponse(w, r, err, "", getRespStatus(err))
return
}
sendAPIResponse(w, r, err, "API key authentication status updated", http.StatusOK)
}
func changeUserPassword(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)

View file

@ -28,6 +28,10 @@ type pwdChange struct {
NewPassword string `json:"new_password"`
}
type apiKeyAuth struct {
AllowAPIKeyAuth bool `json:"allow_api_key_auth"`
}
func sendAPIResponse(w http.ResponseWriter, r *http.Request, err error, message string, code int) {
var errorString string
if _, ok := err.(*util.RecordNotFoundError); ok {

View file

@ -55,6 +55,7 @@ const (
adminPath = "/api/v2/admins"
adminPwdPath = "/api/v2/admin/changepwd"
adminPwdCompatPath = "/api/v2/changepwd/admin"
adminManageAPIKeyPath = "/api/v2/admin/apikeyauth"
userPwdPath = "/api/v2/user/changepwd"
userPublicKeysPath = "/api/v2/user/publickeys"
userFolderPath = "/api/v2/user/folder"
@ -73,6 +74,7 @@ const (
userTOTPValidatePath = "/api/v2/user/totp/validate"
userTOTPSavePath = "/api/v2/user/totp/save"
user2FARecoveryCodesPath = "/api/v2/user/2fa/recoverycodes"
userManageAPIKeyPath = "/api/v2/user/apikeyauth"
healthzPath = "/healthz"
webRootPathDefault = "/"
webBasePathDefault = "/web"

View file

@ -92,11 +92,13 @@ const (
adminTOTPValidatePath = "/api/v2/admin/totp/validate"
adminTOTPSavePath = "/api/v2/admin/totp/save"
admin2FARecoveryCodesPath = "/api/v2/admin/2fa/recoverycodes"
adminManageAPIKeyPath = "/api/v2/admin/apikeyauth"
userTOTPConfigsPath = "/api/v2/user/totp/configs"
userTOTPGeneratePath = "/api/v2/user/totp/generate"
userTOTPValidatePath = "/api/v2/user/totp/validate"
userTOTPSavePath = "/api/v2/user/totp/save"
user2FARecoveryCodesPath = "/api/v2/user/2fa/recoverycodes"
userManageAPIKeyPath = "/api/v2/user/apikeyauth"
healthzPath = "/healthz"
webBasePath = "/web"
webBasePathAdmin = "/web/admin"
@ -3334,6 +3336,17 @@ func TestSkipNaturalKeysValidation(t *testing.T) {
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "the following characters are allowed")
apiKeyAuthReq := make(map[string]bool)
apiKeyAuthReq["allow_api_key_auth"] = true
asJSON, err := json.Marshal(apiKeyAuthReq)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, userManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, userAPIToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "the following characters are allowed")
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
@ -3352,6 +3365,17 @@ func TestSkipNaturalKeysValidation(t *testing.T) {
checkResponseCode(t, http.StatusOK, rr)
assert.Contains(t, rr.Body.String(), "the following characters are allowed")
apiKeyAuthReq = make(map[string]bool)
apiKeyAuthReq["allow_api_key_auth"] = true
asJSON, err = json.Marshal(apiKeyAuthReq)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, adminManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, adminAPIToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
assert.Contains(t, rr.Body.String(), "the following characters are allowed")
req, err = http.NewRequest(http.MethodPut, adminPath+"/"+admin.Username+"/2fa/disable", nil)
assert.NoError(t, err)
setBearerForReq(req, adminAPIToken)
@ -5816,6 +5840,110 @@ func TestWebUserTOTP(t *testing.T) {
checkResponseCode(t, http.StatusNotFound, rr)
}
func TestWebAPIChangeUserAPIKeyAuth(t *testing.T) {
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
assert.NoError(t, err)
assert.False(t, user.Filters.AllowAPIKeyAuth)
token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
// invalid json
req, err := http.NewRequest(http.MethodPut, userManageAPIKeyPath, bytes.NewBuffer([]byte("{")))
assert.NoError(t, err)
setBearerForReq(req, token)
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
apiKeyAuthReq := make(map[string]bool)
apiKeyAuthReq["allow_api_key_auth"] = true
asJSON, err := json.Marshal(apiKeyAuthReq)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, userManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
assert.NoError(t, err)
assert.True(t, user.Filters.AllowAPIKeyAuth)
apiKeyAuthReq = make(map[string]bool)
req, err = http.NewRequest(http.MethodGet, userManageAPIKeyPath, nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
err = json.Unmarshal(rr.Body.Bytes(), &apiKeyAuthReq)
assert.NoError(t, err)
assert.True(t, apiKeyAuthReq["allow_api_key_auth"])
apiKeyAuthReq["allow_api_key_auth"] = false
asJSON, err = json.Marshal(apiKeyAuthReq)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, userManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
user, _, err = httpdtest.GetUserByUsername(user.Username, http.StatusOK)
assert.NoError(t, err)
assert.False(t, user.Filters.AllowAPIKeyAuth)
apiKeyAuthReq = make(map[string]bool)
req, err = http.NewRequest(http.MethodGet, userManageAPIKeyPath, nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
err = json.Unmarshal(rr.Body.Bytes(), &apiKeyAuthReq)
assert.NoError(t, err)
assert.False(t, apiKeyAuthReq["allow_api_key_auth"])
// remove the permission
user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled}
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
assert.Len(t, user.Filters.WebClient, 1)
assert.Contains(t, user.Filters.WebClient, sdk.WebClientAPIKeyAuthChangeDisabled)
newToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
apiKeyAuthReq["allow_api_key_auth"] = true
asJSON, err = json.Marshal(apiKeyAuthReq)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, userManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, newToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusForbidden, rr)
// get will still work
req, err = http.NewRequest(http.MethodGet, userManageAPIKeyPath, nil)
assert.NoError(t, err)
setBearerForReq(req, newToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
apiKeyAuthReq = make(map[string]bool)
req, err = http.NewRequest(http.MethodGet, userManageAPIKeyPath, nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr)
req, err = http.NewRequest(http.MethodPut, userManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr)
}
func TestWebAPIChangeUserPwdMock(t *testing.T) {
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
assert.NoError(t, err)
@ -5890,6 +6018,82 @@ func TestLoginInvalidPasswordMock(t *testing.T) {
assert.Equal(t, http.StatusUnauthorized, rr.Code)
}
func TestChangeAdminAPIKeyAuth(t *testing.T) {
admin := getTestAdmin()
admin.Username = altAdminUsername
admin.Password = altAdminPassword
admin, _, err := httpdtest.AddAdmin(admin, http.StatusCreated)
assert.NoError(t, err)
assert.False(t, admin.Filters.AllowAPIKeyAuth)
token, err := getJWTAPITokenFromTestServer(altAdminUsername, altAdminPassword)
assert.NoError(t, err)
// invalid json
req, err := http.NewRequest(http.MethodPut, adminManageAPIKeyPath, bytes.NewBuffer([]byte("{")))
assert.NoError(t, err)
setBearerForReq(req, token)
rr := executeRequest(req)
checkResponseCode(t, http.StatusBadRequest, rr)
apiKeyAuthReq := make(map[string]bool)
apiKeyAuthReq["allow_api_key_auth"] = true
asJSON, err := json.Marshal(apiKeyAuthReq)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, adminManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
admin, _, err = httpdtest.GetAdminByUsername(altAdminUsername, http.StatusOK)
assert.NoError(t, err)
assert.True(t, admin.Filters.AllowAPIKeyAuth)
apiKeyAuthReq = make(map[string]bool)
req, err = http.NewRequest(http.MethodGet, adminManageAPIKeyPath, nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
err = json.Unmarshal(rr.Body.Bytes(), &apiKeyAuthReq)
assert.NoError(t, err)
assert.True(t, apiKeyAuthReq["allow_api_key_auth"])
apiKeyAuthReq["allow_api_key_auth"] = false
asJSON, err = json.Marshal(apiKeyAuthReq)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, adminManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
apiKeyAuthReq = make(map[string]bool)
req, err = http.NewRequest(http.MethodGet, adminManageAPIKeyPath, nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusOK, rr)
err = json.Unmarshal(rr.Body.Bytes(), &apiKeyAuthReq)
assert.NoError(t, err)
assert.False(t, apiKeyAuthReq["allow_api_key_auth"])
_, err = httpdtest.RemoveAdmin(admin, http.StatusOK)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodGet, adminManageAPIKeyPath, nil)
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr)
req, err = http.NewRequest(http.MethodPut, adminManageAPIKeyPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusNotFound, rr)
}
func TestChangeAdminPwdMock(t *testing.T) {
token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
assert.NoError(t, err)
@ -9482,6 +9686,22 @@ func TestWebUserAllowAPIKeyAuth(t *testing.T) {
assert.NoError(t, err)
assert.False(t, user.Filters.AllowAPIKeyAuth)
user.Filters.WebClient = []string{sdk.WebClientAPIKeyAuthChangeDisabled}
user, _, err = httpdtest.UpdateUser(user, http.StatusOK, "")
assert.NoError(t, err)
assert.False(t, user.CanChangeAPIKeyAuth())
newToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
assert.NoError(t, err)
form = make(url.Values)
form.Set("allow_api_key_auth", "1")
form.Set(csrfFormToken, csrfToken)
req, _ = http.NewRequest(http.MethodPost, webChangeClientAPIKeyAccessPath, bytes.NewBuffer([]byte(form.Encode())))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
setJWTCookieForReq(req, newToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusForbidden, rr)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
_, err = httpdtest.RemoveUser(user, http.StatusOK)

View file

@ -410,6 +410,26 @@ func TestInvalidToken(t *testing.T) {
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Contains(t, rr.Body.String(), "Invalid token claims")
rr = httptest.NewRecorder()
getUserAPIKeyAuthStatus(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Contains(t, rr.Body.String(), "Invalid token claims")
rr = httptest.NewRecorder()
changeUserAPIKeyAuthStatus(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Contains(t, rr.Body.String(), "Invalid token claims")
rr = httptest.NewRecorder()
getAdminAPIKeyAuthStatus(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Contains(t, rr.Body.String(), "Invalid token claims")
rr = httptest.NewRecorder()
changeAdminAPIKeyAuthStatus(rr, req)
assert.Equal(t, http.StatusBadRequest, rr.Code)
assert.Contains(t, rr.Body.String(), "Invalid token claims")
server := httpdServer{}
server.initializeRouter()
rr = httptest.NewRecorder()

View file

@ -242,6 +242,67 @@ paths:
$ref: '#/components/responses/InternalServerError'
default:
$ref: '#/components/responses/DefaultResponse'
/admin/apikeyauth:
get:
security:
- BearerAuth: []
tags:
- admins
summary: Get API key authentication status
description: 'Returns the API Key authentication status for the logged in admin'
operationId: get_admin_api_key_status
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: object
properties:
allow_api_key_auth:
type: boolean
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
default:
$ref: '#/components/responses/DefaultResponse'
put:
security:
- BearerAuth: []
tags:
- admins
summary: Update API key auth status
description: 'Allows to enable/disable the API key authentication for the logged in admin. If enabled, you can impersonate this admin, in REST API, using an API key, otherwise your credentials, including two-factor authentication, if enabled, are required to use the REST API on your behalf'
operationId: update_admin_api_key_status
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
allow_api_key_auth:
type: boolean
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
default:
$ref: '#/components/responses/DefaultResponse'
/admin/2fa/recoverycodes:
get:
security:
@ -2263,6 +2324,67 @@ paths:
$ref: '#/components/responses/InternalServerError'
default:
$ref: '#/components/responses/DefaultResponse'
/user/apikeyauth:
get:
security:
- BearerAuth: []
tags:
- users API
summary: Get API key authentication status
description: 'Returns the API Key authentication status for the logged in user'
operationId: get_user_api_key_status
responses:
'200':
description: successful operation
content:
application/json:
schema:
type: object
properties:
allow_api_key_auth:
type: boolean
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
default:
$ref: '#/components/responses/DefaultResponse'
put:
security:
- BearerAuth: []
tags:
- users API
summary: Update API key auth status
description: 'Allows to enable/disable the API key authentication for the logged in user. If enabled, you can impersonate this user, in REST API, using an API key, otherwise your credentials, including two-factor authentication, if enabled, are required to use the REST API on your behalf'
operationId: update_user_api_key_status
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
allow_api_key_auth:
type: boolean
responses:
'200':
description: successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'400':
$ref: '#/components/responses/BadRequest'
'401':
$ref: '#/components/responses/Unauthorized'
'403':
$ref: '#/components/responses/Forbidden'
'500':
$ref: '#/components/responses/InternalServerError'
default:
$ref: '#/components/responses/DefaultResponse'
/user/2fa/recoverycodes:
get:
security:
@ -3026,12 +3148,14 @@ components:
- write-disabled
- mfa-disabled
- password-change-disabled
- api-key-auth-change-disabled
description: |
Options:
* `publickey-change-disabled` - changing SSH public keys is not allowed
* `write-disabled` - upload, rename, delete are not allowed even if the user has permissions for these actions
* `mfa-disabled` - enabling multi-factor authentication is not allowed. This option cannot be set if the user has MFA already enabled
* `password-change-disabled` - changing password is not allowed
* `api-key-auth-change-disabled` - enabling/disabling API key authentication is not allowed
APIKeyScope:
type: integer
enum:

View file

@ -905,6 +905,8 @@ func (s *httpdServer) initializeRouter() {
})
router.With(forbidAPIKeyAuthentication).Get(logoutPath, s.logout)
router.With(forbidAPIKeyAuthentication).Get(adminManageAPIKeyPath, getAdminAPIKeyAuthStatus)
router.With(forbidAPIKeyAuthentication).Put(adminManageAPIKeyPath, changeAdminAPIKeyAuthStatus)
router.With(forbidAPIKeyAuthentication).Put(adminPwdPath, changeAdminPassword)
// compatibility layer to remove in v2.2
router.With(forbidAPIKeyAuthentication).Put(adminPwdCompatPath, changeAdminPassword)
@ -994,6 +996,9 @@ func (s *httpdServer) initializeRouter() {
Get(userPublicKeysPath, getUserPublicKeys)
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
Put(userPublicKeysPath, setUserPublicKeys)
router.With(forbidAPIKeyAuthentication).Get(userManageAPIKeyPath, getUserAPIKeyAuthStatus)
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientAPIKeyAuthChangeDisabled)).
Put(userManageAPIKeyPath, changeUserAPIKeyAuthStatus)
// user TOTP APIs
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientMFADisabled)).
Get(userTOTPConfigsPath, getTOTPConfigs)
@ -1092,7 +1097,8 @@ func (s *httpdServer) initializeRouter() {
router.With(s.refreshCookie).Get(webClientCredentialsPath, handleClientGetCredentials)
router.With(checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
Post(webChangeClientPwdPath, handleWebClientChangePwdPost)
router.Post(webChangeClientAPIKeyAccessPath, handleWebClientManageAPIKeyPost)
router.With(checkHTTPUserPerm(sdk.WebClientAPIKeyAuthChangeDisabled)).
Post(webChangeClientAPIKeyAccessPath, handleWebClientManageAPIKeyPost)
router.With(checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
Post(webChangeClientKeysPath, handleWebClientManageKeysPost)
router.With(checkHTTPUserPerm(sdk.WebClientMFADisabled), s.refreshCookie).

View file

@ -9,16 +9,17 @@ import (
// Web Client/user REST API restrictions
const (
WebClientPubKeyChangeDisabled = "publickey-change-disabled"
WebClientWriteDisabled = "write-disabled"
WebClientMFADisabled = "mfa-disabled"
WebClientPasswordChangeDisabled = "password-change-disabled"
WebClientPubKeyChangeDisabled = "publickey-change-disabled"
WebClientWriteDisabled = "write-disabled"
WebClientMFADisabled = "mfa-disabled"
WebClientPasswordChangeDisabled = "password-change-disabled"
WebClientAPIKeyAuthChangeDisabled = "api-key-auth-change-disabled"
)
var (
// WebClientOptions defines the available options for the web client interface/user REST API
WebClientOptions = []string{WebClientPubKeyChangeDisabled, WebClientWriteDisabled, WebClientMFADisabled,
WebClientPasswordChangeDisabled}
WebClientPasswordChangeDisabled, WebClientAPIKeyAuthChangeDisabled}
// UserTypes defines the supported user type hints for auth plugins
UserTypes = []string{string(UserTypeLDAP), string(UserTypeOS)}
)

View file

@ -110,7 +110,7 @@
<form id="key_form" action="{{.ManageAPIKeyURL}}" method="POST">
<div class="form-group">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="idAllowAPIKeyAuth" name="allow_api_key_auth"
<input type="checkbox" class="form-check-input" id="idAllowAPIKeyAuth" name="allow_api_key_auth" {{if not .LoggedUser.CanChangeAPIKeyAuth}}disabled="disabled"{{end}}
{{if .AllowAPIKeyAuth}}checked{{end}} aria-describedby="allowAPIKeyAuthHelpBlock">
<label for="idAllowAPIKeyAuth" class="form-check-label">Allow API key authentication</label>
<small id="allowAPIKeyAuthHelpBlock" class="form-text text-muted">
@ -118,9 +118,10 @@
</small>
</div>
</div>
{{if .LoggedUser.CanChangeAPIKeyAuth}}
<input type="hidden" name="_form_token" value="{{.CSRFToken}}">
<button type="submit" class="btn btn-primary float-right mt-3 px-5 px-3">Submit</button>
{{end}}
</form>
</div>
</div>