devices.go 1.86 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"
ale's avatar
ale committed
9
	"git.autistici.org/id/auth/backend"
10 11 12 13 14 15
	"git.autistici.org/id/usermetadb/client"

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

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

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

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

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

ale's avatar
ale committed
36
func (f *deviceFilter) Filter(user *backend.User, req *auth.Request, resp *auth.Response) *auth.Response {
37 38 39 40 41
	// If there is no DeviceInfo, skip.
	if req.DeviceInfo == nil {
		return resp
	}

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

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

ale's avatar
ale committed
68
func (f *deviceFilter) sendNewDeviceEmail(user *backend.User, dev *auth.DeviceInfo) error {
69 70 71 72
	// TODO: Not implemented.
	log.Printf("new device for user %s: %+v", user.Name, dev)
	return nil
}