diff options
author | Derrell Lipman <derrell@samba.org> | 2007-01-07 23:06:50 +0000 |
---|---|---|
committer | Gerald (Jerry) Carter <jerry@samba.org> | 2007-10-10 14:37:13 -0500 |
commit | 9639836022adcb62c72520f799a89d0f727f224d (patch) | |
tree | 26f7331426c94f96f502a8bf8641fb88ffad74d4 /webapps | |
parent | a04a3b8bc21101e6a11bad04c3d5c9655fa606b4 (diff) | |
download | samba-9639836022adcb62c72520f799a89d0f727f224d.tar.gz samba-9639836022adcb62c72520f799a89d0f727f224d.tar.bz2 samba-9639836022adcb62c72520f799a89d0f727f224d.zip |
r20600: Web Application Framework
- Add authentication. The Web Application Framework can now be called
directly and it will rqeuire authentication if required, and should re-query
the user to log in when the session expires.
- General clean-up
(This used to be commit 27c5d7dca6fa4e0811c1b8bb52d1db3d1824462c)
Diffstat (limited to 'webapps')
-rw-r--r-- | webapps/scripting/common.js | 28 | ||||
-rw-r--r-- | webapps/scripting/preauth.esp | 31 | ||||
-rw-r--r-- | webapps/swat/source/class/swat/main/AbstractModuleFsm.js | 251 | ||||
-rw-r--r-- | webapps/swat/source/class/swat/main/Authenticate.js | 152 | ||||
-rw-r--r-- | webapps/swat/source/class/swat/main/Main.js | 10 | ||||
-rw-r--r-- | webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js | 2 | ||||
-rw-r--r-- | webapps/swat/source/class/swat/module/statistics/Fsm.js | 4 | ||||
-rw-r--r-- | webapps/swat/source/class/swat/module/statistics/Gui.js | 2 |
8 files changed, 429 insertions, 51 deletions
diff --git a/webapps/scripting/common.js b/webapps/scripting/common.js index fe25287a74..c111089bd8 100644 --- a/webapps/scripting/common.js +++ b/webapps/scripting/common.js @@ -69,34 +69,6 @@ function page_footer() { /* - check if a uri is one of the 'always allowed' pages, even when not logged in - This allows the login page to use the same style sheets and images -*/ -function always_allowed(uri) { - var str = string_init(); - - /* allow jsonrpc-based applications to do their own authentication */ - var s = str.split('/', uri); - if (s[0] == "" && s[1] == 'apps') { - return true; - } - - var s = str.split('.', uri); - if (s.length < 2) { - return false; - } - - var ext = s[s.length-1]; - var allowed = new Array("ico", "gif", "png","css", "js"); - for (i in allowed) { - if (allowed[i] == ext) { - return true; - } - } - return false; -} - -/* display a table element */ function table_element(i, o) { diff --git a/webapps/scripting/preauth.esp b/webapps/scripting/preauth.esp index 489f6b5004..e6d04faf8d 100644 --- a/webapps/scripting/preauth.esp +++ b/webapps/scripting/preauth.esp @@ -5,6 +5,36 @@ include("/scripting/common.js"); output at all then that output is returned and the requested page is not given or processed. */ + +/* + check if a uri is one of the 'always allowed' pages, even when not logged in + This allows the login page to use the same style sheets and images +*/ +function always_allowed(uri) { + var str = string_init(); + + /* allow jsonrpc-based applications to do their own authentication */ + var s = str.split('/', uri); + if (s[0] == "" && s[1] == 'index.html') { + return true; + } + + var s = str.split('.', uri); + if (s.length < 2) { + return false; + } + + var ext = s[s.length-1]; + var allowed = new Array("ico", "gif", "png","css", "js"); + for (i in allowed) { + if (allowed[i] == ext) { + return true; + } + } + return false; +} + + if (server['SERVER_PROTOCOL'] == "http" && server['TLS_SUPPORT'] == "True") { write("redirect to https"); @@ -14,4 +44,5 @@ if (server['SERVER_PROTOCOL'] == "http" && /* present the login page */ include("/login.esp"); } + %> diff --git a/webapps/swat/source/class/swat/main/AbstractModuleFsm.js b/webapps/swat/source/class/swat/main/AbstractModuleFsm.js index 49222d90d4..fed11eb0d3 100644 --- a/webapps/swat/source/class/swat/main/AbstractModuleFsm.js +++ b/webapps/swat/source/class/swat/main/AbstractModuleFsm.js @@ -109,12 +109,25 @@ qx.Proto.addAwaitRpcResultState = function(module) }, "onentry" : - function(fsm, state) + function(fsm, event) { - // If we're coming from some other state... - if (fsm.getPreviousState() != "State_AwaitRpcResult") + var bAuthCompleted = false; + + // See if we just completed an authentication + if (fsm.getPreviousState() == "State_Authenticate" && + event.getType() == "complete") + { + bAuthCompleted = true; + } +_this.debug("bAuthCompleted=" + bAuthCompleted); + + // If we didn't just complete an authentication and we're coming + // from some other state... + if (! bAuthCompleted && + fsm.getPreviousState() != "State_AwaitRpcResult") { // ... then push the previous state onto the state stack +_this.warn("PUSHING STATE"); fsm.pushState(false); } }, @@ -144,27 +157,63 @@ qx.Proto.addAwaitRpcResultState = function(module) * Cause: "failed" (on RPC) where reason is PermissionDenied */ var trans = new qx.util.fsm.Transition( - "Transition_AwaitRpcResult_to_GetAuthInfo", + "Transition_AwaitRpcResult_to_Authenticate", { "nextState" : - qx.util.fsm.FiniteStateMachine.StateChange.POP_STATE_STACK, + "State_Authenticate", "predicate" : function(fsm, event) { var error = event.getData(); // retrieve the JSON-RPC error - // Did we get get origin=Server, code=PermissionDenied ? + // Did we get get origin=Server, and either + // code=NotLoggedIn or code=SessionExpired ? var origins = swat.main.AbstractModuleFsm.JsonRpc_Origin; var serverErrors = swat.main.AbstractModuleFsm.JsonRpc_ServerError; if (error.origin == origins.Server && - error.code == serverErrors.PermissionDenied) + (error.code == serverErrors.NotLoggedIn || + error.code == serverErrors.SessionExpired)) { return true; } // fall through to next transition, also for "failed" return false; + }, + + "ontransition" : + function(fsm, event) + { + var caption; + + var error = event.getData(); // retrieve the JSON-RPC error + var serverErrors = swat.main.AbstractModuleFsm.JsonRpc_ServerError; + + switch(error.code) + { + case serverErrors.NotLoggedIn: + caption = "Please log in."; + break; + + case serverErrors.SessionExpired: + default: + caption = "Session Expired. Please log in."; + break; + } + + // Retrieve the modal authentication window. + + var loginWin = swat.main.Authenticate.getInstance(module); + + // Set the caption + loginWin.setCaption(caption); + + // Set the domain info + loginWin.setInfo(error.info); + + // Open the authentication window + loginWin.open(); } }); state.addTransition(trans); @@ -247,6 +296,153 @@ qx.Proto.addAwaitRpcResultState = function(module) } }); state.addTransition(trans); + + /* + * State: Authenticate + * + * Transition on: + * "execute" on login_button + */ + var state = new qx.util.fsm.State( + "State_Authenticate", + { + "onentry" : + function(fsm, event) + { + // Retrieve the login window object + var win = module.fsm.getObject("login_window"); + + // Clear the password field + win.password.setValue(""); + + // If there's no value selected for domain... + if (win.domain.getValue() == null) + { + // ... then select the first value + win.domain.setSelected(win.domain.getList().getFirstChild()); + } + + // Retrieve the current RPC request + var rpcRequest = _this.getCurrentRpcRequest(); + + // Did we just return from an RPC request and was it a login request? + if (fsm.getPreviousState() == "State_AwaitRpcResult" && + rpcRequest.service == "samba.system" && + rpcRequest.params.length > 1 && + rpcRequest.params[1] == "login") + { + // Yup. Display the result. Pop the old request off the stack + var loginRequest = _this.popRpcRequest(); + + // Retrieve the result + var result = loginRequest.getUserData("result"); + + // Did we succeed? + if (result.type == "failed") + { + // Nope. Just reset the caption, and remain in this state. + win.setCaption("Login Failed. Try again."); + } + else + { + // Login was successful. Generate an event that will transition + // us back to the AwaitRpcResult state to again await the result + // of the original RPC request. + win.dispatchEvent(new qx.event.type.Event("complete"), true); + + // Reissue the original request. (We already popped the login + // request off the stack, so the current request is the original + // one.) + var origRequest = _this.getCurrentRpcRequest(); + + // Retrieve the RPC object */ + var rpc = fsm.getObject("swat.main.rpc"); + + // Set the service name + rpc.setServiceName(origRequest.service); + + // Reissue the request + origRequest.request = + qx.io.remote.Rpc.prototype.callAsyncListeners.apply( + rpc, + origRequest.params); + + // Clear the password field, for good measure + win.password.setValue(""); + + // Close the login window + win.close(); + } + + // Dispose of the login request + loginRequest.request.dispose(); + loginRequest.request = null; + } + }, + + "events" : + { + "execute" : + { + "login_button" : + "Transition_Authenticate_to_AwaitRpcResult_via_button_login" + }, + + "complete" : + { + "login_window" : + "Transition_Authenticate_to_AwaitRpcResult_via_complete" + } + } + }); + fsm.addState(state); + + /* + * Transition: Authenticate to AwaitRpcResult + * + * Cause: "execute" on login_button + */ + var trans = new qx.util.fsm.Transition( + "Transition_Authenticate_to_AwaitRpcResult_via_button_login", + { + "nextState" : + "State_AwaitRpcResult", + + "ontransition" : + function(fsm, event) + { + // Retrieve the login window object + var win = fsm.getObject("login_window"); + + // Issue a Login call + _this.callRpc(fsm, + "samba.system", + "login", + [ + win.userName.getValue(), + win.password.getValue(), + win.domain.getValue() + ]); + } + }); + state.addTransition(trans); + + /* + * Transition: Authenticate to AwaitRpcResult + * + * Cause: "complete" on login_window + * + * We've already re-issued the original request, so we have nothing to do + * here but transition back to the AwaitRpcResult state to again await the + * result of the original request. + */ + var trans = new qx.util.fsm.Transition( + "Transition_Authenticate_to_AwaitRpcResult_via_complete", + { + "nextState" : + "State_AwaitRpcResult" + }); + state.addTransition(trans); }; @@ -292,7 +488,7 @@ qx.Proto.callRpc = function(fsm, service, method, params) // Set the service name rpc.setServiceName(rpcRequest.service); - // Issue the request, skipping the already-specified service name + // Issue the request rpcRequest.request = qx.io.remote.Rpc.prototype.callAsyncListeners.apply(rpc, rpcRequest.params); @@ -435,21 +631,46 @@ qx.Class.JsonRpc_ServerError = */ PermissionDenied : 6, + /*** Errors generated by this server which are not qooxdoo-standard ***/ + /* - * Error code, value 7: Unexpected Output + * Error code, value 1000: Unexpected Output * * The called method illegally generated output to the browser, which would * have preceeded the JSON-RPC data. */ - UnexpectedOutput : 7, + UnexpectedOutput : 1000, /* - * Error code, value 8: Resource Error + * Error code, value 1001: Resource Error * - * Too many resources were requested, a system limitation on the total - * number of resources has been reached, or a resource or resource id was - * misused. + * Too many resources were requested, a system limitation on the total number + * of resources has been reached, or a resource or resource id was misused. */ - ResourceError : 8 + ResourceError : 1001, + /* + * Error code, value 1002: Not Logged In + * + * The user has logged out and must re-authenticate, or this is a brand new + * session and the user must log in. + * + */ + NotLoggedIn : 1002, + + /* + * Error code, value 1003: Session Expired + * + * The session has expired and the user must re-authenticate. + * + */ + SessionExpired : 1003, + + /* + * Error code, value 1004: Login Failed + * + * An attempt to log in failed. + * + */ + LoginFailed : 1004 }; diff --git a/webapps/swat/source/class/swat/main/Authenticate.js b/webapps/swat/source/class/swat/main/Authenticate.js new file mode 100644 index 0000000000..449a17d9ad --- /dev/null +++ b/webapps/swat/source/class/swat/main/Authenticate.js @@ -0,0 +1,152 @@ +/* + * Copyright: + * (C) 2007 by Derrell Lipman + * All rights reserved + * + * License: + * LGPL 2.1: http://creativecommons.org/licenses/LGPL/2.1/ + */ + +/** + * Swat authentication window class + */ +qx.OO.defineClass("swat.main.Authenticate", qx.ui.window.Window, +function(module) +{ + var o; + var fsm = module.fsm; + + qx.ui.window.Window.call(this); + + var addCaptionedWidget = function(caption, dest, addWidget) + { + // Add a row to the destination grid + dest.addRow(); + var row = dest.getRowCount() - 1; + dest.setRowHeight(row, 24); + + // Add the caption + var o = new qx.ui.basic.Label(caption); + dest.add(o, 0, row); + + // Add the widget + o = addWidget(); + o.setHeight(24); + dest.add(o, 1, row); + + // Give 'em the varying data label + return o; + }; + + + // Set characteristics of this window + this.set({ + width : 380, + height : 200, + modal : true, + centered : true, + showClose : false, + showMaximize : false, + showMinimize : false, + showStatusbar : false, + allowClose : false, + allowMaximize : false, + allowMinimize : false, + resizeable : false, + moveable : false, + zIndex : 10000 + }); + + + // Create a grid layout + var grid = new qx.ui.layout.GridLayout(); + grid.setLocation(14, 14); + grid.setDimension("90%", "90%"); + grid.setVerticalSpacing(14); + grid.setPadding(14, 14); + grid.setRowCount(0); + grid.setColumnCount(2); + grid.setColumnWidth(0, 100); + grid.setColumnWidth(1, 200); + + + // Add an input box for the user name + this.userName = addCaptionedWidget("User Name", grid, + function() + { + return new qx.ui.form.TextField(); + }); + + // Add an input box for the password + this.password = addCaptionedWidget("Password", grid, + function() + { + return new qx.ui.form.PasswordField(); + }); + + // Add an input box for the password + this.domain = addCaptionedWidget("Domain", grid, + function() + { + // Create a combo box for for the domain + var combo = new qx.ui.form.ComboBox(); + combo.setEditable(false); + return combo; + }); + + // Add a login button + this.login = addCaptionedWidget("", grid, + function() + { + return new qx.ui.form.Button("Login"); + }); + + // Save this login button since we receive events on it + fsm.addObject("login_button", this.login); + + // We want to receive "execute" events on this button + this.login.addEventListener("execute", fsm.eventListener, fsm); + + // Add the grid to the window + this.add(grid); + + // Add this window to the document + this.addToDocument(); + + // Save this window object + fsm.addObject("login_window", this); + + // We want to receive "complete" events on this button (which we generate) + this.addEventListener("complete", fsm.eventListener, fsm); +}); + + + +qx.Proto.setInfo = function(info) +{ + this.debug(info); + + // Remove everythingn from the domain list + this.domain.removeAll(); + + // Add the available domains + for (var i = 0; i < info.length; i++) + { + var item = new qx.ui.form.ListItem(info[i]); + this.domain.add(item); + } +}; + + +/** + * Singleton Instance Getter + */ +qx.Class.getInstance = function(module) +{ + if (! this._instance) + { + this._instance = new this(module); + } + + return this._instance; +}; diff --git a/webapps/swat/source/class/swat/main/Main.js b/webapps/swat/source/class/swat/main/Main.js index 7cc6b01736..fda6ba1115 100644 --- a/webapps/swat/source/class/swat/main/Main.js +++ b/webapps/swat/source/class/swat/main/Main.js @@ -10,6 +10,7 @@ /* #require(swat.main.Module) #require(swat.main.AbstractModule) +#require(swat.main.Authenticate); */ /** @@ -22,21 +23,22 @@ function() }); /* - * Register our supported modules + * Register our supported modules. The order listed here is the order they + * will appear in the Modules menu. */ //#require(swat.module.statistics.Statistics) new swat.main.Module("Status and Statistics", - swat.module.statistics.Statistics); + swat.module.statistics.Statistics); //#require(swat.module.ldbbrowse.LdbBrowse) new swat.main.Module("LDB Browser", - swat.module.ldbbrowse.LdbBrowse); + swat.module.ldbbrowse.LdbBrowse); //#require(swat.module.documentation.Documentation) //#require(api.Viewer) new swat.main.Module("API Documentation", - swat.module.documentation.Documentation); + swat.module.documentation.Documentation); /* diff --git a/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js b/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js index 0abb3454c8..4ddc018595 100644 --- a/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js +++ b/webapps/swat/source/class/swat/module/ldbbrowse/Fsm.js @@ -37,7 +37,7 @@ qx.Proto.buildFsm = function(module) "State_Idle", { "onentry" : - function(fsm, state) + function(fsm, event) { // Did we just return from an RPC request? if (fsm.getPreviousState() == "State_AwaitRpcResult") diff --git a/webapps/swat/source/class/swat/module/statistics/Fsm.js b/webapps/swat/source/class/swat/module/statistics/Fsm.js index 3083fed42a..b60501512a 100644 --- a/webapps/swat/source/class/swat/module/statistics/Fsm.js +++ b/webapps/swat/source/class/swat/module/statistics/Fsm.js @@ -61,7 +61,7 @@ qx.Proto.buildFsm = function(module) "State_Idle", { "onentry" : - function(fsm, state) + function(fsm, event) { // Did we just return from an RPC request? if (fsm.getPreviousState() == "State_AwaitRpcResult") @@ -83,7 +83,7 @@ qx.Proto.buildFsm = function(module) }, "onexit" : - function(fsm, state) + function(fsm, event) { // If we're not coming right back into this state... if (fsm.getNextState() != "State_Idle") diff --git a/webapps/swat/source/class/swat/module/statistics/Gui.js b/webapps/swat/source/class/swat/module/statistics/Gui.js index 5968785e07..b5e11d4533 100644 --- a/webapps/swat/source/class/swat/module/statistics/Gui.js +++ b/webapps/swat/source/class/swat/module/statistics/Gui.js @@ -82,7 +82,7 @@ qx.Proto.buildGui = function(module) dest.setRowHeight(row, 16); // Add the caption - o = new qx.ui.basic.Label(caption); + var o = new qx.ui.basic.Label(caption); dest.add(o, 0, row); // Add the text field that will contain varying data |