-
Notifications
You must be signed in to change notification settings - Fork 27
/
do_remove_authenticator.go
108 lines (91 loc) · 3.32 KB
/
do_remove_authenticator.go
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
package nodes
import (
"github.com/authgear/authgear-server/pkg/api"
"github.com/authgear/authgear-server/pkg/api/model"
"github.com/authgear/authgear-server/pkg/lib/authn/authenticator"
"github.com/authgear/authgear-server/pkg/lib/config"
"github.com/authgear/authgear-server/pkg/lib/interaction"
)
func init() {
interaction.RegisterNode(&NodeDoRemoveAuthenticator{})
}
type EdgeDoRemoveAuthenticator struct {
Authenticator *authenticator.Info
BypassMFARequirement bool
}
func (e *EdgeDoRemoveAuthenticator) Instantiate(ctx *interaction.Context, graph *interaction.Graph, rawInput interface{}) (interaction.Node, error) {
return &NodeDoRemoveAuthenticator{
Authenticator: e.Authenticator,
BypassMFARequirement: e.BypassMFARequirement,
}, nil
}
type NodeDoRemoveAuthenticator struct {
Authenticator *authenticator.Info `json:"authenticator"`
BypassMFARequirement bool `json:"bypass_mfa_requirement"`
}
func (n *NodeDoRemoveAuthenticator) Prepare(ctx *interaction.Context, graph *interaction.Graph) error {
return nil
}
// nolint:gocognit
func (n *NodeDoRemoveAuthenticator) GetEffects() ([]interaction.Effect, error) {
return []interaction.Effect{
interaction.EffectRun(func(ctx *interaction.Context, graph *interaction.Graph, nodeIndex int) error {
userID := graph.MustGetUserID()
as, err := ctx.Authenticators.List(userID)
if err != nil {
return err
}
switch n.Authenticator.Kind {
case authenticator.KindPrimary:
if n.Authenticator.Type == model.AuthenticatorTypePasskey {
return api.NewInvariantViolated(
"RemovePasskeyAuthenticator",
"cannot delete passkey authenticator, should delete passkey identity instead",
nil,
)
}
// Ensure all identities have matching primary authenticator.
is, err := ctx.Identities.ListByUser(userID)
if err != nil {
return err
}
for _, i := range is {
primaryAuths := authenticator.ApplyFilters(as, authenticator.KeepPrimaryAuthenticatorOfIdentity(i))
if len(primaryAuths) == 1 && primaryAuths[0].ID == n.Authenticator.ID {
return api.NewInvariantViolated(
"RemoveLastPrimaryAuthenticator",
"cannot remove last primary authenticator for identity",
map[string]interface{}{"identity_id": i.ID},
)
}
}
case authenticator.KindSecondary:
// Ensure authenticators conform to MFA requirement configuration
if n.BypassMFARequirement {
break
}
primaries := authenticator.ApplyFilters(as, authenticator.KeepPrimaryAuthenticatorCanHaveMFA)
secondaries := authenticator.ApplyFilters(as, authenticator.KeepKind(authenticator.KindSecondary))
mode := ctx.Config.Authentication.SecondaryAuthenticationMode
cannotRemove := mode == config.SecondaryAuthenticationModeRequired &&
len(primaries) > 0 &&
len(secondaries) == 1 && secondaries[0].ID == n.Authenticator.ID
if cannotRemove {
return api.NewInvariantViolated(
"RemoveLastSecondaryAuthenticator",
"cannot remove last secondary authenticator",
nil,
)
}
}
err = ctx.Authenticators.Delete(n.Authenticator)
if err != nil {
return err
}
return nil
}),
}, nil
}
func (n *NodeDoRemoveAuthenticator) DeriveEdges(graph *interaction.Graph) ([]interaction.Edge, error) {
return graph.Intent.DeriveEdgesForNode(graph, n)
}