21
21
*/
22
22
package org .exist .xquery .modules .persistentlogin ;
23
23
24
+ import org .checkerframework .checker .nullness .qual .Nullable ;
24
25
import org .exist .EXistException ;
25
26
import org .exist .dom .QName ;
26
27
import org .exist .security .AuthenticationException ;
30
31
import org .exist .xquery .*;
31
32
import org .exist .xquery .value .*;
32
33
34
+ import java .util .EnumSet ;
35
+ import java .util .HashMap ;
36
+ import java .util .Map ;
37
+
33
38
/**
34
39
* Functions to access the persistent login module.
35
40
*/
36
41
public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
42
+ private enum Fn {
43
+ REGISTER ("register" ),
44
+ LOGIN ("login" ),
45
+ INVALIDATE ("invalidate" );
46
+
47
+ final static Map <QName , Fn > lookup = new HashMap <>();
48
+ static {
49
+ for (Fn fn : EnumSet .allOf (Fn .class )) {
50
+ lookup .put (fn .getQName (), fn );
51
+ }
52
+ }
53
+
54
+ static Fn get (Function f ) {
55
+ return lookup .get (f .getName ());
56
+ }
57
+
58
+ private final QName qname ;
59
+
60
+ Fn (String name ) {
61
+ qname = new QName (name , PersistentLoginModule .NAMESPACE , PersistentLoginModule .PREFIX );
62
+ }
37
63
38
- public final static FunctionSignature signatures [] = {
64
+ public QName getQName () { return qname ; }
65
+ }
66
+
67
+ public final static FunctionSignature [] signatures = {
39
68
new FunctionSignature (
40
- new QName ( "register" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
69
+ Fn . REGISTER . getQName ( ),
41
70
"Try to log in the user and create a one-time login token. The token can be stored to a cookie and used to log in " +
42
71
"(via the login function) as the same user without " +
43
72
"providing credentials. However, for security reasons the token will be valid only for " +
@@ -55,7 +84,7 @@ public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
55
84
new FunctionReturnSequenceType (Type .ITEM , Cardinality .ZERO_OR_MORE , "result of the callback function or the empty sequence" )
56
85
),
57
86
new FunctionSignature (
58
- new QName ( "login" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
87
+ Fn . LOGIN . getQName ( ),
59
88
"Try to log in the user based on the supplied token. If the login succeeds, the provided callback function " +
60
89
"is called with 4 arguments: $token as xs:string, $user as xs:string, $password as xs:string, $timeToLive as duration. " +
61
90
"$token will be a new token which can be used for the next request. The old token is deleted." ,
@@ -67,7 +96,7 @@ public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
67
96
new FunctionReturnSequenceType (Type .ITEM , Cardinality .ZERO_OR_MORE , "result of the callback function or the empty sequence" )
68
97
),
69
98
new FunctionSignature (
70
- new QName ( "invalidate" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
99
+ Fn . INVALIDATE . getQName ( ),
71
100
"Invalidate the supplied one-time token, so it can no longer be used to log in." ,
72
101
new SequenceType []{
73
102
new FunctionParameterSequenceType ("token" , Type .STRING , Cardinality .EXACTLY_ONE , "a valid one-time token" )
@@ -85,77 +114,59 @@ public PersistentLoginFunctions(final XQueryContext context, final FunctionSigna
85
114
@ Override
86
115
public void analyze (final AnalyzeContextInfo contextInfo ) throws XPathException {
87
116
super .analyze (contextInfo );
88
- this . cachedContextInfo = new AnalyzeContextInfo (contextInfo );
117
+ cachedContextInfo = new AnalyzeContextInfo (contextInfo );
89
118
}
90
119
91
120
@ Override
92
121
public Sequence eval (final Sequence [] args , final Sequence contextSequence ) throws XPathException {
93
- if (isCalledAs ("register" )) {
94
- final String user = args [0 ].getStringValue ();
95
- final String pass ;
96
- if (!args [1 ].isEmpty ()) {
97
- pass = args [1 ].getStringValue ();
98
- } else {
99
- pass = null ;
100
- }
101
- final DurationValue timeToLive = (DurationValue ) args [2 ].itemAt (0 );
102
- final FunctionReference callback ;
103
- if (!args [3 ].isEmpty ()) {
104
- callback = (FunctionReference ) args [3 ].itemAt (0 );
105
- } else {
106
- callback = null ;
107
- }
108
- try {
109
- return register (user , pass , timeToLive , callback );
110
- } finally {
111
- if (callback != null ) {
112
- callback .close ();
113
- }
114
- }
115
- } else if (isCalledAs ("login" )) {
116
- final String token = args [0 ].getStringValue ();
117
- final FunctionReference callback ;
118
- if (!args [1 ].isEmpty ()) {
119
- callback = (FunctionReference ) args [1 ].itemAt (0 );
120
- } else {
121
- callback = null ;
122
- }
123
- try {
124
- return authenticate (token , callback );
125
- } finally {
126
- if (callback != null ) {
127
- callback .close ();
128
- }
129
- }
130
- } else {
131
- PersistentLogin .getInstance ().invalidate (args [0 ].getStringValue ());
132
- return Sequence .EMPTY_SEQUENCE ;
122
+ switch (Fn .get (this )) {
123
+ case REGISTER : return register (args );
124
+ case LOGIN : return login (args );
125
+ case INVALIDATE : return invalidate (args );
126
+ default : throw new XPathException (this , ErrorCodes .ERROR , "Unknown function: " + getName ());
133
127
}
134
128
}
135
129
136
- private Sequence register (final String user , final String pass , final DurationValue timeToLive , final FunctionReference callback ) throws XPathException {
137
- if (login (user , pass )) {
130
+ private Sequence register (Sequence [] args ) throws XPathException {
131
+ final String user = args [0 ].getStringValue ();
132
+
133
+ final String pass ;
134
+ if (args [1 ].isEmpty ()) {
135
+ pass = null ;
136
+ } else {
137
+ pass = args [1 ].getStringValue ();
138
+ }
139
+
140
+ final DurationValue timeToLive = (DurationValue ) args [2 ].itemAt (0 );
141
+
142
+ try (FunctionReference callback = getCallBack (args [3 ])) {
143
+ if (!authenticated (user , pass )) {
144
+ return Sequence .EMPTY_SEQUENCE ;
145
+ }
138
146
final PersistentLogin .LoginDetails details = PersistentLogin .getInstance ().register (user , pass , timeToLive );
139
- return callback (callback , null , details );
147
+ return call (callback , null , details );
140
148
}
141
- return Sequence .EMPTY_SEQUENCE ;
142
149
}
143
150
144
- private Sequence authenticate (final String token , final FunctionReference callback ) throws XPathException {
145
- final PersistentLogin .LoginDetails data = PersistentLogin .getInstance ().lookup (token );
151
+ private Sequence login (Sequence [] args ) throws XPathException {
152
+ final String token = args [0 ].getStringValue ();
153
+ try (FunctionReference callback = getCallBack (args [1 ])) {
154
+ final PersistentLogin .LoginDetails data = PersistentLogin .getInstance ().lookup (token );
146
155
147
- if (data == null ) {
148
- return Sequence .EMPTY_SEQUENCE ;
149
- }
156
+ if (data == null || !authenticated (data .getUser (), data .getPassword ())) {
157
+ return Sequence .EMPTY_SEQUENCE ;
158
+ }
159
+ return call (callback , token , data );
150
160
151
- if (login (data .getUser (), data .getPassword ())) {
152
- return callback (callback , token , data );
153
161
}
162
+ }
154
163
164
+ private static Sequence invalidate (Sequence [] args ) throws XPathException {
165
+ PersistentLogin .getInstance ().invalidate (args [0 ].getStringValue ());
155
166
return Sequence .EMPTY_SEQUENCE ;
156
167
}
157
168
158
- private boolean login (final String user , final String pass ) throws XPathException {
169
+ private boolean authenticated (final String user , final String pass ) {
159
170
try {
160
171
final SecurityManager sm = BrokerPool .getInstance ().getSecurityManager ();
161
172
final Subject subject = sm .authenticate (user , pass );
@@ -169,7 +180,7 @@ private boolean login(final String user, final String pass) throws XPathExceptio
169
180
}
170
181
}
171
182
172
- private Sequence callback ( final FunctionReference func , final String oldToken , final PersistentLogin .LoginDetails details ) throws XPathException {
183
+ private Sequence call ( @ Nullable final FunctionReference func , final String oldToken , final PersistentLogin .LoginDetails details ) throws XPathException {
173
184
final Sequence [] args = new Sequence [4 ];
174
185
final String newToken = details .toString ();
175
186
@@ -185,4 +196,11 @@ private Sequence callback(final FunctionReference func, final String oldToken, f
185
196
func .analyze (cachedContextInfo );
186
197
return func .evalFunction (null , null , args );
187
198
}
199
+
200
+ private @ Nullable FunctionReference getCallBack (final Sequence arg ) {
201
+ if (arg .isEmpty ()) {
202
+ return null ;
203
+ }
204
+ return (FunctionReference ) arg .itemAt (0 );
205
+ }
188
206
}
0 commit comments