web client UI: add a permission to disable password change

Fixes #528
This commit is contained in:
Nicola Murino 2021-09-05 18:49:13 +02:00
parent 59140a6d51
commit 101c2962ab
No known key found for this signature in database
GPG key ID: 2F1FB59433D5A8CB
7 changed files with 79 additions and 20 deletions

View file

@ -719,6 +719,11 @@ func (u *User) CanManageMFA() bool {
return len(mfa.GetAvailableTOTPConfigs()) > 0
}
// CanChangePassword returns true if this user is allowed to change its password
func (u *User) CanChangePassword() bool {
return !util.IsStringInSlice(sdk.WebClientPasswordChangeDisabled, 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

@ -304,19 +304,6 @@ then SFTPGo will try to create `id_rsa`, `id_ecdsa` and `id_ed25519`, if they ar
The configuration can be read from JSON, TOML, YAML, HCL, envfile and Java properties config files. If your `config-file` flag is set to `sftpgo` (default value), you need to create a configuration file called `sftpgo.json` or `sftpgo.yaml` and so on inside `config-dir`.
## Binding to privileged ports
On Linux, if you want to use Internet domain privileged ports (port numbers less than 1024) instead of running the SFTPGo service as root user you can set the `cap_net_bind_service` capability on the `sftpgo` binary. To set the capability you can use the following command:
```shell
$ sudo setcap cap_net_bind_service=+ep /usr/bin/sftpgo
# Check that the capability is added:
$ getcap /usr/bin/sftpgo
/usr/bin/sftpgo cap_net_bind_service=ep
```
Now you can use privileged ports such as 21, 22, 443 etc.. without running the SFTPGo service as root user. You have to set the `cap_net_bind_service` capability each time you update the `sftpgo` binary.
## Environment variables
You can also override all the available configuration options using environment variables. SFTPGo will check for environment variables with a name matching the key uppercased and prefixed with the `SFTPGO_`. You need to use `__` to traverse a struct.
@ -335,6 +322,26 @@ You can select `sha256-simd` setting the environment variable `SFTPGO_MINIO_SHA2
`sha256-simd` is particularly useful if you have an Intel CPU with SHA extensions or an ARM CPU with Cryptography Extensions.
## Binding to privileged ports
On Linux, if you want to use Internet domain privileged ports (port numbers less than 1024) instead of running the SFTPGo service as root user you can set the `cap_net_bind_service` capability on the `sftpgo` binary. To set the capability you can use the following command:
```shell
$ sudo setcap cap_net_bind_service=+ep /usr/bin/sftpgo
# Check that the capability is added
$ getcap /usr/bin/sftpgo
/usr/bin/sftpgo cap_net_bind_service=ep
```
Now you can use privileged ports such as 21, 22, 443 etc.. without running the SFTPGo service as root user. You have to set the `cap_net_bind_service` capability each time you update the `sftpgo` binary.
An alternative method is to use `iptables`, for example you run the SFTP service on port `2022` and redirect traffic from port `22` to port `2022`:
```shell
sudo iptables -t nat -A PREROUTING -d <ip> -p tcp --dport 22 -m addrtype --dst-type LOCAL -j DNAT --to-destination <ip>:2022
sudo iptables -t nat -A OUTPUT -d <ip> -p tcp --dport 22 -m addrtype --dst-type LOCAL -j DNAT --to-destination <ip>:2022
```
## Telemetry Server
The telemetry server exposes the following endpoints:

View file

@ -5854,6 +5854,27 @@ func TestWebAPIChangeUserPwdMock(t *testing.T) {
assert.NoError(t, err)
assert.NotEmpty(t, token)
// remove the change password permission
user.Filters.WebClient = []string{sdk.WebClientPasswordChangeDisabled}
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.WebClientPasswordChangeDisabled)
token, err = getJWTAPIUserTokenFromTestServer(defaultUsername, altAdminPassword)
assert.NoError(t, err)
assert.NotEmpty(t, token)
pwd["current_password"] = altAdminPassword
pwd["new_password"] = defaultPassword
asJSON, err = json.Marshal(pwd)
assert.NoError(t, err)
req, err = http.NewRequest(http.MethodPut, userPwdPath, bytes.NewBuffer(asJSON))
assert.NoError(t, err)
setBearerForReq(req, token)
rr = executeRequest(req)
checkResponseCode(t, http.StatusForbidden, rr)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())
@ -7568,6 +7589,24 @@ func TestWebClientChangePwd(t *testing.T) {
_, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword+"1")
assert.NoError(t, err)
// remove the change password permission
user.Filters.WebClient = []string{sdk.WebClientPasswordChangeDisabled}
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.WebClientPasswordChangeDisabled)
webToken, err = getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword+"1")
assert.NoError(t, err)
form.Set("current_password", defaultPassword+"1")
form.Set("new_password1", defaultPassword)
form.Set("new_password2", defaultPassword)
req, _ = http.NewRequest(http.MethodPost, webChangeClientPwdPath, bytes.NewBuffer([]byte(form.Encode())))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
setJWTCookieForReq(req, webToken)
rr = executeRequest(req)
checkResponseCode(t, http.StatusForbidden, rr)
_, err = httpdtest.RemoveUser(user, http.StatusOK)
assert.NoError(t, err)
err = os.RemoveAll(user.GetHomeDir())

View file

@ -3025,11 +3025,13 @@ components:
- publickey-change-disabled
- write-disabled
- mfa-disabled
- password-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` - the user cannot enable multi-factor authentication. This option cannot be set if the user has MFA already enabled
* `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
APIKeyScope:
type: integer
enum:

View file

@ -988,7 +988,8 @@ func (s *httpdServer) initializeRouter() {
router.Use(jwtAuthenticatorAPIUser)
router.With(forbidAPIKeyAuthentication).Get(userLogoutPath, s.logout)
router.With(forbidAPIKeyAuthentication).Put(userPwdPath, changeUserPassword)
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
Put(userPwdPath, changeUserPassword)
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
Get(userPublicKeysPath, getUserPublicKeys)
router.With(forbidAPIKeyAuthentication, checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
@ -1089,7 +1090,8 @@ func (s *httpdServer) initializeRouter() {
Delete(webClientDirsPath, deleteUserDir)
router.With(s.refreshCookie).Get(webClientDownloadZipPath, handleWebClientDownloadZip)
router.With(s.refreshCookie).Get(webClientCredentialsPath, handleClientGetCredentials)
router.Post(webChangeClientPwdPath, handleWebClientChangePwdPost)
router.With(checkHTTPUserPerm(sdk.WebClientPasswordChangeDisabled)).
Post(webChangeClientPwdPath, handleWebClientChangePwdPost)
router.Post(webChangeClientAPIKeyAccessPath, handleWebClientManageAPIKeyPost)
router.With(checkHTTPUserPerm(sdk.WebClientPubKeyChangeDisabled)).
Post(webChangeClientKeysPath, handleWebClientManageKeysPost)

View file

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

View file

@ -4,6 +4,7 @@
{{define "page_body"}}
{{if .LoggedUser.CanChangePassword}}
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Change password</h6>
@ -41,6 +42,7 @@
</form>
</div>
</div>
{{end}}
{{if .LoggedUser.CanManagePublicKeys}}
<div class="card shadow mb-4">
<div class="card-header py-3">