-
Notifications
You must be signed in to change notification settings - Fork 3.4k
Expand file tree
/
Copy pathmfa_model.go
More file actions
158 lines (130 loc) · 3.92 KB
/
mfa_model.go
File metadata and controls
158 lines (130 loc) · 3.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package core
import (
"context"
"errors"
"fmt"
"time"
"github.com/pocketbase/pocketbase/tools/hook"
"github.com/pocketbase/pocketbase/tools/types"
)
const (
MFAMethodPassword = "password"
MFAMethodOAuth2 = "oauth2"
MFAMethodOTP = "otp"
)
const CollectionNameMFAs = "_mfas"
var (
_ Model = (*MFA)(nil)
_ PreValidator = (*MFA)(nil)
_ RecordProxy = (*MFA)(nil)
)
// MFA defines a Record proxy for working with the mfas collection.
type MFA struct {
*Record
}
// NewMFA instantiates and returns a new blank *MFA model.
//
// Example usage:
//
// mfa := core.NewMFA(app)
// mfa.SetRecordRef(user.Id)
// mfa.SetCollectionRef(user.Collection().Id)
// mfa.SetMethod(core.MFAMethodPassword)
// app.Save(mfa)
func NewMFA(app App) *MFA {
m := &MFA{}
c, err := app.FindCachedCollectionByNameOrId(CollectionNameMFAs)
if err != nil {
// this is just to make tests easier since mfa is a system collection and it is expected to be always accessible
// (note: the loaded record is further checked on MFA.PreValidate())
c = NewBaseCollection("@__invalid__")
}
m.Record = NewRecord(c)
return m
}
// PreValidate implements the [PreValidator] interface and checks
// whether the proxy is properly loaded.
func (m *MFA) PreValidate(ctx context.Context, app App) error {
if m.Record == nil || m.Record.Collection().Name != CollectionNameMFAs {
return errors.New("missing or invalid mfa ProxyRecord")
}
return nil
}
// ProxyRecord returns the proxied Record model.
func (m *MFA) ProxyRecord() *Record {
return m.Record
}
// SetProxyRecord loads the specified record model into the current proxy.
func (m *MFA) SetProxyRecord(record *Record) {
m.Record = record
}
// CollectionRef returns the "collectionRef" field value.
func (m *MFA) CollectionRef() string {
return m.GetString("collectionRef")
}
// SetCollectionRef updates the "collectionRef" record field value.
func (m *MFA) SetCollectionRef(collectionId string) {
m.Set("collectionRef", collectionId)
}
// RecordRef returns the "recordRef" record field value.
func (m *MFA) RecordRef() string {
return m.GetString("recordRef")
}
// SetRecordRef updates the "recordRef" record field value.
func (m *MFA) SetRecordRef(recordId string) {
m.Set("recordRef", recordId)
}
// Method returns the "method" record field value.
func (m *MFA) Method() string {
return m.GetString("method")
}
// SetMethod updates the "method" record field value.
func (m *MFA) SetMethod(method string) {
m.Set("method", method)
}
// Created returns the "created" record field value.
func (m *MFA) Created() types.DateTime {
return m.GetDateTime("created")
}
// Updated returns the "updated" record field value.
func (m *MFA) Updated() types.DateTime {
return m.GetDateTime("updated")
}
// HasExpired checks if the mfa is expired, aka. whether it has been
// more than maxElapsed time since its creation.
func (m *MFA) HasExpired(maxElapsed time.Duration) bool {
return time.Since(m.Created().Time()) > maxElapsed
}
func (app *BaseApp) registerMFAHooks() {
recordRefHooks[*MFA](app, CollectionNameMFAs, CollectionTypeAuth)
// run on every hour to cleanup expired mfa sessions
app.Cron().Add("__pbMFACleanup__", "0 * * * *", func() {
if err := app.DeleteExpiredMFAs(); err != nil {
app.Logger().Warn("Failed to delete expired MFA sessions", "error", err)
}
})
// delete existing mfas on password change
app.OnRecordUpdate().Bind(&hook.Handler[*RecordEvent]{
Func: func(e *RecordEvent) error {
err := e.Next()
if err != nil || !e.Record.Collection().IsAuth() {
return err
}
old := e.Record.Original().GetString(FieldNamePassword + ":hash")
new := e.Record.GetString(FieldNamePassword + ":hash")
if old != new {
err = e.App.DeleteAllMFAsByRecord(e.Record)
if err != nil {
return fmt.Errorf(
"[%s] failed to delete all previos MFAs for record %q: %w",
e.Record.Collection().Name,
e.Record.Id,
err,
)
}
}
return nil
},
Priority: 99,
})
}