16159: Makes test login form to autofocus on username field.
[arvados.git] / lib / controller / localdb / login_testuser.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package localdb
6
7 import (
8         "bytes"
9         "context"
10         "fmt"
11         "html/template"
12
13         "git.arvados.org/arvados.git/lib/controller/rpc"
14         "git.arvados.org/arvados.git/sdk/go/arvados"
15         "git.arvados.org/arvados.git/sdk/go/ctxlog"
16         "github.com/sirupsen/logrus"
17 )
18
19 type testLoginController struct {
20         Cluster *arvados.Cluster
21         Parent  *Conn
22 }
23
24 func (ctrl *testLoginController) Logout(ctx context.Context, opts arvados.LogoutOptions) (arvados.LogoutResponse, error) {
25         err := ctrl.Parent.ExpireAPIClientAuthorization(ctx)
26         if err != nil {
27                 return arvados.LogoutResponse{}, err
28         }
29         return noopLogout(ctrl.Cluster, opts)
30 }
31
32 func (ctrl *testLoginController) Login(ctx context.Context, opts arvados.LoginOptions) (arvados.LoginResponse, error) {
33         tmpl, err := template.New("form").Parse(loginform)
34         if err != nil {
35                 return arvados.LoginResponse{}, err
36         }
37         var buf bytes.Buffer
38         err = tmpl.Execute(&buf, opts)
39         if err != nil {
40                 return arvados.LoginResponse{}, err
41         }
42         return arvados.LoginResponse{HTML: buf}, nil
43 }
44
45 func (ctrl *testLoginController) UserAuthenticate(ctx context.Context, opts arvados.UserAuthenticateOptions) (arvados.APIClientAuthorization, error) {
46         for username, user := range ctrl.Cluster.Login.Test.Users {
47                 if (opts.Username == username || opts.Username == user.Email) && opts.Password == user.Password {
48                         ctxlog.FromContext(ctx).WithFields(logrus.Fields{
49                                 "username": username,
50                                 "email":    user.Email,
51                         }).Debug("test authentication succeeded")
52                         return ctrl.Parent.CreateAPIClientAuthorization(ctx, ctrl.Cluster.SystemRootToken, rpc.UserSessionAuthInfo{
53                                 Username: username,
54                                 Email:    user.Email,
55                         })
56                 }
57         }
58         return arvados.APIClientAuthorization{}, fmt.Errorf("authentication failed for user %q with password len=%d", opts.Username, len(opts.Password))
59 }
60
61 const loginform = `
62 <!doctype html>
63 <html>
64   <head><title>Arvados test login</title>
65     <script>
66       async function authenticate(event) {
67         event.preventDefault()
68         document.getElementById('error').innerHTML = ''
69         const resp = await fetch('/arvados/v1/users/authenticate', {
70           method: 'POST',
71           mode: 'same-origin',
72           headers: {'Content-Type': 'application/json'},
73           body: JSON.stringify({
74             username: document.getElementById('username').value,
75             password: document.getElementById('password').value,
76           }),
77         })
78         if (!resp.ok) {
79           document.getElementById('error').innerHTML = '<p>Authentication failed.</p><p>The "test login" users are defined in Clusters.[ClusterID].Login.Test.Users section of config.yml</p><p>If you are using arvbox, use "arvbox adduser" to add users.</p>'
80           return
81         }
82         var redir = document.getElementById('return_to').value
83         if (redir.indexOf('?') > 0) {
84           redir += '&'
85         } else {
86           redir += '?'
87         }
88         const respj = await resp.json()
89         document.location = redir + "api_token=v2/" + respj.uuid + "/" + respj.api_token
90       }
91     </script>
92   </head>
93   <body>
94     <h3>Arvados test login</h3>
95     <form method="POST">
96       <input id="return_to" type="hidden" name="return_to" value="{{.ReturnTo}}">
97       username <input id="username" type="text" name="username" autofocus size=16>
98       password <input id="password" type="password" name="password" size=16>
99       <input type="submit" value="Log in">
100       <br>
101       <p id="error"></p>
102     </form>
103   </body>
104   <script>
105     document.getElementsByTagName('form')[0].onsubmit = authenticate
106   </script>
107 </html>
108 `