2727import org .exist .security .SecurityManager ;
2828import org .exist .security .Subject ;
2929import 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 ;
3252
3353/**
3454 * Functions to access the persistent login module.
3555 */
3656public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
37-
38- public final static FunctionSignature signatures [] = {
57+ public final static FunctionSignature [] signatures = {
3958 new FunctionSignature (
40- new QName ( "register" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
59+ PersistentLoginFn . REGISTER . getQName ( ),
4160 "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 " +
4261 "(via the login function) as the same user without " +
4362 "providing credentials. However, for security reasons the token will be valid only for " +
@@ -55,7 +74,7 @@ public class PersistentLoginFunctions extends UserSwitchingBasicFunction {
5574 new FunctionReturnSequenceType (Type .ITEM , Cardinality .ZERO_OR_MORE , "result of the callback function or the empty sequence" )
5675 ),
5776 new FunctionSignature (
58- new QName ( "login" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
77+ PersistentLoginFn . LOGIN . getQName ( ),
5978 "Try to log in the user based on the supplied token. If the login succeeds, the provided callback function " +
6079 "is called with 4 arguments: $token as xs:string, $user as xs:string, $password as xs:string, $timeToLive as duration. " +
6180 "$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 {
6786 new FunctionReturnSequenceType (Type .ITEM , Cardinality .ZERO_OR_MORE , "result of the callback function or the empty sequence" )
6887 ),
6988 new FunctionSignature (
70- new QName ( "invalidate" , PersistentLoginModule . NAMESPACE , PersistentLoginModule . PREFIX ),
89+ PersistentLoginFn . INVALIDATE . getQName ( ),
7190 "Invalidate the supplied one-time token, so it can no longer be used to log in." ,
7291 new SequenceType []{
7392 new FunctionParameterSequenceType ("token" , Type .STRING , Cardinality .EXACTLY_ONE , "a valid one-time token" )
7493 },
7594 new FunctionReturnSequenceType (Type .EMPTY , Cardinality .EXACTLY_ONE , "empty sequence" )
7695 )
7796 };
78-
7997 private AnalyzeContextInfo cachedContextInfo ;
8098
8199 public PersistentLoginFunctions (final XQueryContext context , final FunctionSignature signature ) {
82100 super (context , signature );
83101 }
84102
103+ private static Sequence invalidate (Sequence [] args ) throws XPathException {
104+ PersistentLogin .getInstance ().invalidate (args [0 ].getStringValue ());
105+ return Sequence .EMPTY_SEQUENCE ;
106+ }
107+
85108 @ Override
86109 public void analyze (final AnalyzeContextInfo contextInfo ) throws XPathException {
87110 super .analyze (contextInfo );
88- this . cachedContextInfo = new AnalyzeContextInfo (contextInfo );
111+ cachedContextInfo = new AnalyzeContextInfo (contextInfo );
89112 }
90113
91114 @ Override
92115 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 ());
133125 }
134126 }
135127
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 ();
140136 }
141- return Sequence .EMPTY_SEQUENCE ;
142- }
143137
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 );
146139
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 );
149146 }
147+ }
150148
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 );
154153
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+ }
156159 }
157160
158- private boolean login (final String user , final String pass ) throws XPathException {
161+ private boolean unauthenticated (final String user , final String pass ) {
159162 try {
160163 final SecurityManager sm = BrokerPool .getInstance ().getSecurityManager ();
161164 final Subject subject = sm .authenticate (user , pass );
162165
163166 //switch the user of the current broker
164167 switchUser (subject );
165168
166- return true ;
167- } catch (final AuthenticationException | EXistException e ) {
168169 return false ;
170+ } catch (final AuthenticationException | EXistException e ) {
171+ return true ;
169172 }
170173 }
171174
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 ;
173177 final Sequence [] args = new Sequence [4 ];
174178 final String newToken = details .toString ();
175179
@@ -185,4 +189,39 @@ private Sequence callback(final FunctionReference func, final String oldToken, f
185189 func .analyze (cachedContextInfo );
186190 return func .evalFunction (null , null , args );
187191 }
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+ }
188227}
0 commit comments