27
27
import org .exist .security .SecurityManager ;
28
28
import org .exist .security .Subject ;
29
29
import org .exist .storage .BrokerPool ;
30
- import org .exist .xquery .*;
31
- import org .exist .xquery .value .*;
30
+ import org .exist .xquery .AnalyzeContextInfo ;
31
+ import org .exist .xquery .Cardinality ;
32
+ import org .exist .xquery .ErrorCodes ;
33
+ import org .exist .xquery .Function ;
34
+ import org .exist .xquery .FunctionSignature ;
35
+ import org .exist .xquery .UserSwitchingBasicFunction ;
36
+ import org .exist .xquery .XPathException ;
37
+ import org .exist .xquery .XQueryContext ;
38
+ import org .exist .xquery .value .DurationValue ;
39
+ import org .exist .xquery .value .FunctionParameterSequenceType ;
40
+ import org .exist .xquery .value .FunctionReference ;
41
+ import org .exist .xquery .value .FunctionReturnSequenceType ;
42
+ import org .exist .xquery .value .Sequence ;
43
+ import org .exist .xquery .value .SequenceType ;
44
+ import org .exist .xquery .value .StringValue ;
45
+ import org .exist .xquery .value .Type ;
46
+
47
+ import javax .annotation .Nullable ;
48
+
49
+ import java .util .EnumSet ;
50
+ import java .util .HashMap ;
51
+ import java .util .Map ;
32
52
33
53
/**
34
54
* Functions to access the persistent login module.
35
55
*/
36
56
public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
37
-
38
- public final static FunctionSignature signatures [] = {
57
+ public final static FunctionSignature [] signatures = {
39
58
new FunctionSignature (
40
- new QName ( "register" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
59
+ PersistentLoginFn . REGISTER . getQName ( ),
41
60
"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
61
"(via the login function) as the same user without " +
43
62
"providing credentials. However, for security reasons the token will be valid only for " +
@@ -55,7 +74,7 @@ public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
55
74
new FunctionReturnSequenceType (Type .ITEM , Cardinality .ZERO_OR_MORE , "result of the callback function or the empty sequence" )
56
75
),
57
76
new FunctionSignature (
58
- new QName ( "login" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
77
+ PersistentLoginFn . LOGIN . getQName ( ),
59
78
"Try to log in the user based on the supplied token. If the login succeeds, the provided callback function " +
60
79
"is called with 4 arguments: $token as xs:string, $user as xs:string, $password as xs:string, $timeToLive as duration. " +
61
80
"$token will be a new token which can be used for the next request. The old token is deleted." ,
@@ -67,109 +86,94 @@ public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
67
86
new FunctionReturnSequenceType (Type .ITEM , Cardinality .ZERO_OR_MORE , "result of the callback function or the empty sequence" )
68
87
),
69
88
new FunctionSignature (
70
- new QName ( "invalidate" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
89
+ PersistentLoginFn . INVALIDATE . getQName ( ),
71
90
"Invalidate the supplied one-time token, so it can no longer be used to log in." ,
72
91
new SequenceType []{
73
92
new FunctionParameterSequenceType ("token" , Type .STRING , Cardinality .EXACTLY_ONE , "a valid one-time token" )
74
93
},
75
94
new FunctionReturnSequenceType (Type .EMPTY , Cardinality .EXACTLY_ONE , "empty sequence" )
76
95
)
77
96
};
78
-
79
97
private AnalyzeContextInfo cachedContextInfo ;
80
98
81
99
public PersistentLoginFunctions (final XQueryContext context , final FunctionSignature signature ) {
82
100
super (context , signature );
83
101
}
84
102
103
+ private static Sequence invalidate (Sequence [] args ) throws XPathException {
104
+ PersistentLogin .getInstance ().invalidate (args [0 ].getStringValue ());
105
+ return Sequence .EMPTY_SEQUENCE ;
106
+ }
107
+
85
108
@ Override
86
109
public void analyze (final AnalyzeContextInfo contextInfo ) throws XPathException {
87
110
super .analyze (contextInfo );
88
- this . cachedContextInfo = new AnalyzeContextInfo (contextInfo );
111
+ cachedContextInfo = new AnalyzeContextInfo (contextInfo );
89
112
}
90
113
91
114
@ Override
92
115
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 ;
116
+ switch (PersistentLoginFn .get (this )) {
117
+ case REGISTER :
118
+ return register (args );
119
+ case LOGIN :
120
+ return login (args );
121
+ case INVALIDATE :
122
+ return invalidate (args );
123
+ default :
124
+ throw new XPathException (this , ErrorCodes .ERROR , "Unknown function: " + getName ());
133
125
}
134
126
}
135
127
136
- private Sequence register (final String user , final String pass , final DurationValue timeToLive , final FunctionReference callback ) throws XPathException {
137
- if (login (user , pass )) {
138
- final PersistentLogin .LoginDetails details = PersistentLogin .getInstance ().register (user , pass , timeToLive );
139
- return callback (callback , null , details );
128
+ private Sequence register (Sequence [] args ) throws XPathException {
129
+ final String user = args [0 ].getStringValue ();
130
+
131
+ final String pass ;
132
+ if (args [1 ].isEmpty ()) {
133
+ pass = null ;
134
+ } else {
135
+ pass = args [1 ].getStringValue ();
140
136
}
141
- return Sequence .EMPTY_SEQUENCE ;
142
- }
143
137
144
- private Sequence authenticate (final String token , final FunctionReference callback ) throws XPathException {
145
- final PersistentLogin .LoginDetails data = PersistentLogin .getInstance ().lookup (token );
138
+ final DurationValue timeToLive = (DurationValue ) args [2 ].itemAt (0 );
146
139
147
- if (data == null ) {
148
- return Sequence .EMPTY_SEQUENCE ;
140
+ try (FunctionReference callback = getCallBack (args [3 ])) {
141
+ if (unauthenticated (user , pass )) {
142
+ return Sequence .EMPTY_SEQUENCE ;
143
+ }
144
+ final PersistentLogin .LoginDetails details = PersistentLogin .getInstance ().register (user , pass , timeToLive );
145
+ return call (callback , null , details );
149
146
}
147
+ }
150
148
151
- if (login (data .getUser (), data .getPassword ())) {
152
- return callback (callback , token , data );
153
- }
149
+ private Sequence login (Sequence [] args ) throws XPathException {
150
+ final String token = args [0 ].getStringValue ();
151
+ try (FunctionReference callback = getCallBack (args [1 ])) {
152
+ final PersistentLogin .LoginDetails data = PersistentLogin .getInstance ().lookup (token );
154
153
155
- return Sequence .EMPTY_SEQUENCE ;
154
+ if (data == null || unauthenticated (data .getUser (), data .getPassword ())) {
155
+ return Sequence .EMPTY_SEQUENCE ;
156
+ }
157
+ return call (callback , token , data );
158
+ }
156
159
}
157
160
158
- private boolean login (final String user , final String pass ) throws XPathException {
161
+ private boolean unauthenticated (final String user , final String pass ) {
159
162
try {
160
163
final SecurityManager sm = BrokerPool .getInstance ().getSecurityManager ();
161
164
final Subject subject = sm .authenticate (user , pass );
162
165
163
166
//switch the user of the current broker
164
167
switchUser (subject );
165
168
166
- return true ;
167
- } catch (final AuthenticationException | EXistException e ) {
168
169
return false ;
170
+ } catch (final AuthenticationException | EXistException e ) {
171
+ return true ;
169
172
}
170
173
}
171
174
172
- private Sequence callback (final FunctionReference func , final String oldToken , final PersistentLogin .LoginDetails details ) throws XPathException {
175
+ private Sequence call (@ Nullable final FunctionReference func , final String oldToken , final PersistentLogin .LoginDetails details ) throws XPathException {
176
+ if (func == null ) return Sequence .EMPTY_SEQUENCE ;
173
177
final Sequence [] args = new Sequence [4 ];
174
178
final String newToken = details .toString ();
175
179
@@ -185,4 +189,39 @@ private Sequence callback(final FunctionReference func, final String oldToken, f
185
189
func .analyze (cachedContextInfo );
186
190
return func .evalFunction (null , null , args );
187
191
}
192
+
193
+ private @ Nullable FunctionReference getCallBack (final Sequence arg ) {
194
+ if (arg .isEmpty ()) {
195
+ return null ;
196
+ }
197
+ return (FunctionReference ) arg .itemAt (0 );
198
+ }
199
+
200
+ private enum PersistentLoginFn {
201
+ REGISTER ("register" ),
202
+ LOGIN ("login" ),
203
+ INVALIDATE ("invalidate" );
204
+
205
+ final static Map <QName , PersistentLoginFn > lookup = new HashMap <>();
206
+
207
+ static {
208
+ for (PersistentLoginFn persistentLoginFn : EnumSet .allOf (PersistentLoginFn .class )) {
209
+ lookup .put (persistentLoginFn .getQName (), persistentLoginFn );
210
+ }
211
+ }
212
+
213
+ private final QName qname ;
214
+
215
+ PersistentLoginFn (String name ) {
216
+ qname = new QName (name , PersistentLoginModule .NAMESPACE , PersistentLoginModule .PREFIX );
217
+ }
218
+
219
+ static PersistentLoginFn get (Function f ) {
220
+ return lookup .get (f .getName ());
221
+ }
222
+
223
+ public QName getQName () {
224
+ return qname ;
225
+ }
226
+ }
188
227
}
0 commit comments