16314: Serve login form in test login mode.
authorTom Clegg <tom@tomclegg.ca>
Mon, 24 Aug 2020 21:05:25 +0000 (17:05 -0400)
committerTom Clegg <tom@tomclegg.ca>
Mon, 24 Aug 2020 21:05:25 +0000 (17:05 -0400)
Arvados-DCO-1.1-Signed-off-by: Tom Clegg <tom@tomclegg.ca>

lib/controller/localdb/login_testuser.go
lib/controller/localdb/login_testuser_test.go

index 5a3d803b89960df03dce638291af9cb338560d78..823043702a134b72342d58b8685abe56fe19ee05 100644 (file)
@@ -5,9 +5,10 @@
 package localdb
 
 import (
+       "bytes"
        "context"
-       "errors"
        "fmt"
+       "html/template"
 
        "git.arvados.org/arvados.git/lib/controller/rpc"
        "git.arvados.org/arvados.git/sdk/go/arvados"
@@ -25,7 +26,16 @@ func (ctrl *testLoginController) Logout(ctx context.Context, opts arvados.Logout
 }
 
 func (ctrl *testLoginController) Login(ctx context.Context, opts arvados.LoginOptions) (arvados.LoginResponse, error) {
-       return arvados.LoginResponse{}, errors.New("interactive login is not available")
+       tmpl, err := template.New("form").Parse(loginform)
+       if err != nil {
+               return arvados.LoginResponse{}, err
+       }
+       var buf bytes.Buffer
+       err = tmpl.Execute(&buf, opts)
+       if err != nil {
+               return arvados.LoginResponse{}, err
+       }
+       return arvados.LoginResponse{HTML: buf}, nil
 }
 
 func (ctrl *testLoginController) UserAuthenticate(ctx context.Context, opts arvados.UserAuthenticateOptions) (arvados.APIClientAuthorization, error) {
@@ -43,3 +53,52 @@ func (ctrl *testLoginController) UserAuthenticate(ctx context.Context, opts arva
        }
        return arvados.APIClientAuthorization{}, fmt.Errorf("authentication failed for user %q with password len=%d", opts.Username, len(opts.Password))
 }
+
+const loginform = `
+<!doctype html>
+<html>
+  <head><title>Arvados test login</title>
+    <script>
+      async function authenticate(event) {
+        event.preventDefault()
+       document.getElementById('error').innerHTML = ''
+       const resp = await fetch('/arvados/v1/users/authenticate', {
+         method: 'POST',
+         mode: 'same-origin',
+         headers: {'Content-Type': 'application/json'},
+         body: JSON.stringify({
+           username: document.getElementById('username').value,
+           password: document.getElementById('password').value,
+         }),
+       })
+       if (!resp.ok) {
+         document.getElementById('error').innerHTML = 'authentication failed (default accounts are user/user, admin/admin)'
+         return
+       }
+       var redir = document.getElementById('return_to').value
+       if (redir.indexOf('?') > 0) {
+         redir += '&'
+       } else {
+         redir += '?'
+       }
+        const respj = await resp.json()
+       document.location = redir + "api_token=" + respj.api_token
+      }
+    </script>
+  </head>
+  <body>
+    <h3>Arvados test login</h3>
+    <form method="POST">
+      <input id="return_to" type="hidden" name="return_to" value="{{.ReturnTo}}">
+      username <input id="username" type="text" name="username" size=16>
+      password <input id="password" type="password" name="password" size=16>
+      <input type="submit" value="Log in">
+      <br>
+      <p id="error"></p>
+    </form>
+  </body>
+  <script>
+    document.getElementsByTagName('form')[0].onsubmit = authenticate
+  </script>
+</html>
+`
index d2d651e205ca5b668d80ef838f74f5dc0ce9bc03..7589088899744efca9187e2cc9d3094b8d39db03 100644 (file)
@@ -92,3 +92,12 @@ func (s *TestUserSuite) TestLogin(c *check.C) {
                }
        }
 }
+
+func (s *TestUserSuite) TestLoginForm(c *check.C) {
+       resp, err := s.ctrl.Login(s.ctx, arvados.LoginOptions{
+               ReturnTo: "https://localhost:12345/example",
+       })
+       c.Check(err, check.IsNil)
+       c.Check(resp.HTML.String(), check.Matches, `(?ms).*<form method="POST".*`)
+       c.Check(resp.HTML.String(), check.Matches, `(?ms).*<input id="return_to" type="hidden" name="return_to" value="https://localhost:12345/example">.*`)
+}