|
@@ -3401,6 +3401,107 @@ func TestTwoFactorRequirements(t *testing.T) {
|
|
|
assert.NoError(t, err)
|
|
|
}
|
|
|
|
|
|
+func TestTwoFactorRequirementsGroupLevel(t *testing.T) {
|
|
|
+ g := getTestGroup()
|
|
|
+ g.UserSettings.Filters.TwoFactorAuthProtocols = []string{common.ProtocolHTTP, common.ProtocolFTP}
|
|
|
+ group, _, err := httpdtest.AddGroup(g, http.StatusCreated)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ u := getTestUser()
|
|
|
+ u.Groups = []sdk.GroupMapping{
|
|
|
+ {
|
|
|
+ Name: group.Name,
|
|
|
+ Type: sdk.GroupTypePrimary,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ user, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
|
|
+ assert.NoError(t, err)
|
|
|
+
|
|
|
+ token, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
|
|
|
+ assert.NoError(t, err)
|
|
|
+
|
|
|
+ req, err := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ req.RequestURI = webClientFilesPath
|
|
|
+ setJWTCookieForReq(req, webToken)
|
|
|
+ rr := executeRequest(req)
|
|
|
+ checkResponseCode(t, http.StatusForbidden, rr)
|
|
|
+ assert.Contains(t, rr.Body.String(), util.I18nError2FARequired)
|
|
|
+
|
|
|
+ req, err = http.NewRequest(http.MethodGet, userDirsPath, nil)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ setBearerForReq(req, token)
|
|
|
+ rr = executeRequest(req)
|
|
|
+ checkResponseCode(t, http.StatusForbidden, rr)
|
|
|
+ assert.Contains(t, rr.Body.String(), "Two-factor authentication requirements not met, please configure two-factor authentication for the following protocols")
|
|
|
+
|
|
|
+ configName, key, _, err := mfa.GenerateTOTPSecret(mfa.GetAvailableTOTPConfigNames()[0], user.Username)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ userTOTPConfig := dataprovider.UserTOTPConfig{
|
|
|
+ Enabled: true,
|
|
|
+ ConfigName: configName,
|
|
|
+ Secret: kms.NewPlainSecret(key.Secret()),
|
|
|
+ Protocols: []string{common.ProtocolHTTP},
|
|
|
+ }
|
|
|
+ asJSON, err := json.Marshal(userTOTPConfig)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
|
|
|
+ assert.NoError(t, err)
|
|
|
+ setBearerForReq(req, token)
|
|
|
+ rr = executeRequest(req)
|
|
|
+ checkResponseCode(t, http.StatusBadRequest, rr)
|
|
|
+ assert.Contains(t, rr.Body.String(), "the following protocols are required")
|
|
|
+
|
|
|
+ userTOTPConfig = dataprovider.UserTOTPConfig{
|
|
|
+ Enabled: true,
|
|
|
+ ConfigName: configName,
|
|
|
+ Secret: kms.NewPlainSecret(key.Secret()),
|
|
|
+ Protocols: []string{common.ProtocolFTP, common.ProtocolHTTP},
|
|
|
+ }
|
|
|
+ asJSON, err = json.Marshal(userTOTPConfig)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ req, err = http.NewRequest(http.MethodPost, userTOTPSavePath, bytes.NewBuffer(asJSON))
|
|
|
+ assert.NoError(t, err)
|
|
|
+ setBearerForReq(req, token)
|
|
|
+ rr = executeRequest(req)
|
|
|
+ checkResponseCode(t, http.StatusOK, rr)
|
|
|
+
|
|
|
+ // now get new tokens and check that the two factor requirements are now met
|
|
|
+ passcode, err := generateTOTPPasscode(key.Secret())
|
|
|
+ assert.NoError(t, err)
|
|
|
+ req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userTokenPath), nil)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ req.Header.Set("X-SFTPGO-OTP", passcode)
|
|
|
+ req.SetBasicAuth(defaultUsername, defaultPassword)
|
|
|
+ resp, err := httpclient.GetHTTPClient().Do(req)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
+ responseHolder := make(map[string]any)
|
|
|
+ err = render.DecodeJSON(resp.Body, &responseHolder)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ userToken := responseHolder["access_token"].(string)
|
|
|
+ assert.NotEmpty(t, userToken)
|
|
|
+ err = resp.Body.Close()
|
|
|
+ assert.NoError(t, err)
|
|
|
+
|
|
|
+ req, err = http.NewRequest(http.MethodGet, fmt.Sprintf("%v%v", httpBaseURL, userDirsPath), nil)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ setBearerForReq(req, userToken)
|
|
|
+ resp, err = httpclient.GetHTTPClient().Do(req)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ assert.Equal(t, http.StatusOK, resp.StatusCode)
|
|
|
+ err = resp.Body.Close()
|
|
|
+ assert.NoError(t, err)
|
|
|
+
|
|
|
+ _, err = httpdtest.RemoveUser(user, http.StatusOK)
|
|
|
+ assert.NoError(t, err)
|
|
|
+ err = os.RemoveAll(user.GetHomeDir())
|
|
|
+ assert.NoError(t, err)
|
|
|
+ _, err = httpdtest.RemoveGroup(group, http.StatusOK)
|
|
|
+ assert.NoError(t, err)
|
|
|
+}
|
|
|
+
|
|
|
func TestLoginUserAPITOTP(t *testing.T) {
|
|
|
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
|
|
assert.NoError(t, err)
|