devices.go 1.81 KB
Newer Older
1
2
3
4
5
6
7
package server

import (
	"context"
	"log"
	"time"

ale's avatar
ale committed
8
	"git.autistici.org/ai3/go-common/clientutil"
9
10
11
12
13
14
	"git.autistici.org/id/usermetadb/client"

	"git.autistici.org/id/auth"
)

// Inject an interface for testing purposes.
ale's avatar
ale committed
15
type checkDeviceClient interface {
ale's avatar
ale committed
16
	CheckDevice(context.Context, string, string, *auth.DeviceInfo) (bool, error)
17
18
19
}

type deviceFilter struct {
ale's avatar
ale committed
20
	client checkDeviceClient
21
22
}

23
24
25
// The timeout for this RPC is very short, as it needs to be performed
// synchronously with the authentication request.
var deviceCheckTimeout = 3 * time.Second
26

ale's avatar
ale committed
27
func newDeviceFilter(config *clientutil.BackendConfig) (*deviceFilter, error) {
ale's avatar
ale committed
28
	c, err := client.New(config)
29
30
31
32
33
34
35
36
37
38
39
40
	if err != nil {
		return nil, err
	}
	return &deviceFilter{c}, nil
}

func (f *deviceFilter) Filter(user *User, req *auth.Request, resp *auth.Response) *auth.Response {
	// If there is no DeviceInfo, skip.
	if req.DeviceInfo == nil {
		return resp
	}

41
42
43
44
45
	// If the status is != OK, skip.
	if resp.Status != auth.StatusOK {
		return resp
	}

46
47
	// Check if the device is known already, in which case we're
	// OK and don't need to do anything else.
48
	ctx, cancel := context.WithTimeout(context.Background(), deviceCheckTimeout)
49
	defer cancel()
ale's avatar
ale committed
50
	seen, err := f.client.CheckDevice(ctx, user.Shard, user.Name, req.DeviceInfo)
51
	if err != nil {
52
		log.Printf("usermetadb.CheckDevice error for %s: %v", user.Name, err)
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
		return resp
	}

	if !seen {
		// New device! Send out a warning and store it for the future.
		// Errors are logged but non-fatal.
		if err := f.sendNewDeviceEmail(user, req.DeviceInfo); err != nil {
			log.Printf("error sending new device email to %s: %v", user.Name, err)
		}
	}

	return resp
}

func (f *deviceFilter) sendNewDeviceEmail(user *User, dev *auth.DeviceInfo) error {
	// TODO: Not implemented.
	log.Printf("new device for user %s: %+v", user.Name, dev)
	return nil
}