19973: Merge branch 'main'
[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         return logout(ctx, ctrl.Cluster, opts)
26 }
27
28 func (ctrl *testLoginController) Login(ctx context.Context, opts arvados.LoginOptions) (arvados.LoginResponse, error) {
29         tmpl, err := template.New("form").Parse(loginform)
30         if err != nil {
31                 return arvados.LoginResponse{}, err
32         }
33         var buf bytes.Buffer
34         err = tmpl.Execute(&buf, opts)
35         if err != nil {
36                 return arvados.LoginResponse{}, err
37         }
38         return arvados.LoginResponse{HTML: buf}, nil
39 }
40
41 func (ctrl *testLoginController) UserAuthenticate(ctx context.Context, opts arvados.UserAuthenticateOptions) (arvados.APIClientAuthorization, error) {
42         for username, user := range ctrl.Cluster.Login.Test.Users {
43                 if (opts.Username == username || opts.Username == user.Email) && opts.Password == user.Password {
44                         ctxlog.FromContext(ctx).WithFields(logrus.Fields{
45                                 "username": username,
46                                 "email":    user.Email,
47                         }).Debug("test authentication succeeded")
48                         return ctrl.Parent.CreateAPIClientAuthorization(ctx, ctrl.Cluster.SystemRootToken, rpc.UserSessionAuthInfo{
49                                 Username: username,
50                                 Email:    user.Email,
51                         })
52                 }
53         }
54         return arvados.APIClientAuthorization{}, fmt.Errorf("authentication failed for user %q with password len=%d", opts.Username, len(opts.Password))
55 }
56
57 const loginform = `
58 <!doctype html>
59 <html>
60   <head><title>Arvados test login</title>
61     <script>
62       async function authenticate(event) {
63         event.preventDefault()
64         document.getElementById('error').innerHTML = ''
65         const resp = await fetch('/arvados/v1/users/authenticate', {
66           method: 'POST',
67           mode: 'same-origin',
68           headers: {'Content-Type': 'application/json'},
69           body: JSON.stringify({
70             username: document.getElementById('username').value,
71             password: document.getElementById('password').value,
72           }),
73         })
74         if (!resp.ok) {
75           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>'
76           return
77         }
78         var redir = document.getElementById('return_to').value
79         if (redir.indexOf('?') > 0) {
80           redir += '&'
81         } else {
82           redir += '?'
83         }
84         const respj = await resp.json()
85         document.location = redir + "api_token=v2/" + respj.uuid + "/" + respj.api_token
86       }
87     </script>
88   </head>
89   <body>
90     <h3>Arvados test login</h3>
91     <form method="POST">
92       <input id="return_to" type="hidden" name="return_to" value="{{.ReturnTo}}">
93       username <input id="username" type="text" name="username" autofocus size=16>
94       password <input id="password" type="password" name="password" size=16>
95       <input type="submit" value="Log in">
96       <br>
97       <p id="error"></p>
98     </form>
99   </body>
100   <script>
101     document.getElementsByTagName('form')[0].onsubmit = authenticate
102   </script>
103 </html>
104 `