summaryrefslogtreecommitdiff
path: root/internal/command/twofactorverify/twofactorverifymanual_test.go
diff options
context:
space:
mode:
authorkmcknight <kmcknight@gitlab.com>2021-02-25 17:17:25 -0800
committerIgor Drozdov <idrozdov@gitlab.com>2022-07-18 07:28:32 +0200
commitfe5feeea22a639a4835724cf42b337773b54d83c (patch)
tree9dd8851a2d05460c713c46227e8365244eda3022 /internal/command/twofactorverify/twofactorverifymanual_test.go
parent9cacca57e5300c9a23dfc9ae18f48ee48483451b (diff)
downloadgitlab-shell-fe5feeea22a639a4835724cf42b337773b54d83c.tar.gz
Implement Push Auth support for 2FA verification
When `2fa_verify` command is executed: - A user is asked to enter OTP - A blocking call for push auth is performed Then: - If the push auth request fails, the user is still able to enter OTP - If OTP is invalid, the `2fa_verify` command ends the execution - If OTP is valid or push auth request succeeded, then the user is successfully authenticated - If 30 seconds passed while no OTP or Push have been provided, then the `2fa_verify` command ends the execution
Diffstat (limited to 'internal/command/twofactorverify/twofactorverifymanual_test.go')
-rw-r--r--internal/command/twofactorverify/twofactorverifymanual_test.go154
1 files changed, 154 insertions, 0 deletions
diff --git a/internal/command/twofactorverify/twofactorverifymanual_test.go b/internal/command/twofactorverify/twofactorverifymanual_test.go
new file mode 100644
index 0000000..59db281
--- /dev/null
+++ b/internal/command/twofactorverify/twofactorverifymanual_test.go
@@ -0,0 +1,154 @@
+package twofactorverify
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "io"
+ "net/http"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/require"
+
+ "gitlab.com/gitlab-org/gitlab-shell/v14/client/testserver"
+ "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/commandargs"
+ "gitlab.com/gitlab-org/gitlab-shell/v14/internal/command/readwriter"
+ "gitlab.com/gitlab-org/gitlab-shell/v14/internal/config"
+ "gitlab.com/gitlab-org/gitlab-shell/v14/internal/gitlabnet/twofactorverify"
+)
+
+func setupManual(t *testing.T) []testserver.TestRequestHandler {
+ requests := []testserver.TestRequestHandler{
+ {
+ Path: "/api/v4/internal/two_factor_manual_otp_check",
+ Handler: func(w http.ResponseWriter, r *http.Request) {
+ b, err := io.ReadAll(r.Body)
+ defer r.Body.Close()
+
+ require.NoError(t, err)
+
+ var requestBody *twofactorverify.RequestBody
+ require.NoError(t, json.Unmarshal(b, &requestBody))
+
+ var body map[string]interface{}
+ switch requestBody.KeyId {
+ case "1":
+ body = map[string]interface{}{
+ "success": true,
+ }
+ json.NewEncoder(w).Encode(body)
+ case "error":
+ body = map[string]interface{}{
+ "success": false,
+ "message": "error message",
+ }
+ require.NoError(t, json.NewEncoder(w).Encode(body))
+ case "broken":
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+ },
+ },
+ {
+ Path: "/api/v4/internal/two_factor_push_otp_check",
+ Handler: func(w http.ResponseWriter, r *http.Request) {
+ b, err := io.ReadAll(r.Body)
+ defer r.Body.Close()
+
+ require.NoError(t, err)
+
+ var requestBody *twofactorverify.RequestBody
+ require.NoError(t, json.Unmarshal(b, &requestBody))
+
+ time.Sleep(5 * time.Second)
+
+ var body map[string]interface{}
+ switch requestBody.KeyId {
+ case "1":
+ body = map[string]interface{}{
+ "success": true,
+ }
+ json.NewEncoder(w).Encode(body)
+ case "error":
+ body = map[string]interface{}{
+ "success": false,
+ "message": "error message",
+ }
+ require.NoError(t, json.NewEncoder(w).Encode(body))
+ case "broken":
+ w.WriteHeader(http.StatusInternalServerError)
+ default:
+ body = map[string]interface{}{
+ "success": true,
+ "message": "default message",
+ }
+ json.NewEncoder(w).Encode(body)
+ }
+ },
+ },
+ }
+
+ return requests
+}
+
+const (
+ manualQuestion = "OTP: \n"
+ manualErrorHeader = "OTP validation failed.\n"
+)
+
+func TestExecuteManual(t *testing.T) {
+ requests := setupManual(t)
+
+ url := testserver.StartSocketHttpServer(t, requests)
+
+ testCases := []struct {
+ desc string
+ arguments *commandargs.Shell
+ answer string
+ expectedOutput string
+ }{
+ {
+ desc: "With a known key id",
+ arguments: &commandargs.Shell{GitlabKeyId: "1"},
+ answer: "123456\n",
+ expectedOutput: manualQuestion + "OTP validation successful. Git operations are now allowed.\n",
+ },
+ {
+ desc: "With bad response",
+ arguments: &commandargs.Shell{GitlabKeyId: "-1"},
+ answer: "123456\n",
+ expectedOutput: manualQuestion + manualErrorHeader + "Parsing failed\n",
+ },
+ {
+ desc: "With API returns an error",
+ arguments: &commandargs.Shell{GitlabKeyId: "error"},
+ answer: "yes\n",
+ expectedOutput: manualQuestion + manualErrorHeader + "error message\n",
+ },
+ {
+ desc: "With API fails",
+ arguments: &commandargs.Shell{GitlabKeyId: "broken"},
+ answer: "yes\n",
+ expectedOutput: manualQuestion + manualErrorHeader + "Internal API error (500)\n",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.desc, func(t *testing.T) {
+ output := &bytes.Buffer{}
+
+ input := bytes.NewBufferString(tc.answer)
+
+ cmd := &Command{
+ Config: &config.Config{GitlabUrl: url},
+ Args: tc.arguments,
+ ReadWriter: &readwriter.ReadWriter{Out: output, In: input},
+ }
+
+ err := cmd.Execute(context.Background())
+
+ require.NoError(t, err)
+ require.Equal(t, tc.expectedOutput, output.String())
+ })
+ }
+}