/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.scripting; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.mozilla.javascript.annotations.JSFunction; import com.servoy.base.scripting.api.IJSSecurity; import com.servoy.j2db.ApplicationException; import com.servoy.j2db.IApplication; import com.servoy.j2db.dataprocessing.BufferedDataSet; import com.servoy.j2db.dataprocessing.ClientInfo; import com.servoy.j2db.dataprocessing.IDataSet; import com.servoy.j2db.dataprocessing.JSDataSet; import com.servoy.j2db.documentation.ServoyDocumented; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.IPersist; import com.servoy.j2db.persistence.IRepository; import com.servoy.j2db.persistence.IServer; import com.servoy.j2db.persistence.ISupportName; import com.servoy.j2db.persistence.ITable; import com.servoy.j2db.persistence.RepositoryException; import com.servoy.j2db.persistence.SolutionMetaData; import com.servoy.j2db.persistence.Table; import com.servoy.j2db.scripting.info.FORMSECURITY; import com.servoy.j2db.scripting.info.TABLESECURITY; import com.servoy.j2db.util.DataSourceUtils; import com.servoy.j2db.util.Debug; import com.servoy.j2db.util.ServoyException; import com.servoy.j2db.util.UUID; import com.servoy.j2db.util.Utils; /** * JavaScript Object to handle security inside servoy * * @author jcompagner,seb,jblok */ @ServoyDocumented(category = ServoyDocumented.RUNTIME, publicName = "Security", scriptingName = "security") public class JSSecurity implements IReturnedTypesProvider, IConstantsObject, IJSSecurity { static { ScriptObjectRegistry.registerReturnedTypesProviderForClass(JSSecurity.class, new IReturnedTypesProvider() { public Class< ? >[] getAllReturnedTypes() { return new Class< ? >[] { TABLESECURITY.class, FORMSECURITY.class }; } }); } /** * Constant representing the read flag for table security. * * @sampleas js_setSecuritySettings(Object) */ public static final int READ = IRepository.READ; /** * Constant representing the insert flag for table security. * * @sampleas js_setSecuritySettings(Object) */ public static final int INSERT = IRepository.INSERT; /** * Constant representing the update flag for table security. * * @sampleas js_setSecuritySettings(Object) */ public static final int UPDATE = IRepository.UPDATE; /** * Constant representing the delete flag for table security. * * @sampleas js_setSecuritySettings(Object) */ public static final int DELETE = IRepository.DELETE; /** * Constant representing the tracking flag for table security (tracks sql insert/update/delete). * * @sampleas js_setSecuritySettings(Object) */ public static final int TRACKING = IRepository.TRACKING; /** * Constant representing the tracking flag for table security (tracks sql select). * * @sampleas js_setSecuritySettings(Object) */ public static final int TRACKING_VIEWS = IRepository.TRACKING_VIEWS; /** * Constant representing the viewable flag for form security. * * @sampleas js_setSecuritySettings(Object) */ public static final int VIEWABLE = IRepository.VIEWABLE; /** * Constant representing the accessible flag for form security. * * @sampleas js_setSecuritySettings(Object) */ public static final int ACCESSIBLE = IRepository.ACCESSIBLE; private volatile IApplication application; public JSSecurity(IApplication application) { this.application = application; } /** * Returns the client ID. * * @sample var clientId = security.getClientID() * @return the clientId as seen on the server admin page */ public String js_getClientID() { return application.getClientID(); } /** * @clonedesc js_getUserUID(String) * @sampleas js_getUserUID(String) * * @return the userUID */ public String js_getUserUID() throws ServoyException { return js_getUserUID(null); } /** * Get the current user UID (null if not logged in); finds the userUID for given user_name if passed as parameter. * * @sample * //gets the current loggedIn username * var userName = security.getUserName(); * //gets the uid of the given username * var userUID = security.getUserUID(userName); * //is the same as above * //var my_userUID = security.getUserUID(); * * @param username the username to find the userUID for * * @return the userUID */ public String js_getUserUID(String username) throws ServoyException { checkAuthorized(); try { if (username == null || username.length() == 0) { // No user name specified, try logged in user. return application.getUserUID(); } else { return application.getUserManager().getUserUID(application.getClientID(), username); } } catch (Exception e) { Debug.error(e); } return null; } /** * @deprecated As of release 3.0, replaced by {@link #getUserUID(String)}. */ @Deprecated public Object js_getUserId(Object[] args) throws ServoyException { if (args == null || args.length != 1) return js_getUserUID(); else if (args[0] instanceof String) return js_getUserUID((String)args[0]); return null; } //group id's are meaningless pk's (not stable across repositories); don't expose /** * Returns the Group ID for the specified group (name) * * @deprecated Deprecated as of release 3.0, not supported anymore. * * @sample * //returns the groupid for the admin group in the variable * //var groupIDForAdmin = security.getGroupId('admin') */ @Deprecated public Object js_getGroupId(String groupName) throws ServoyException { checkAuthorized(); int gid = getGroupId(groupName); if (gid != -1) { return new Integer(gid); } return null; } private int getGroupId(String groupName) { try { int groupId = application.getUserManager().getGroupId(application.getClientID(), groupName); return groupId; } catch (Exception e) { Debug.error(e); } return -1; } /** * Retrieves the username of the currently logged in user on operating system level. * * @sample * //gets the current os username * var osUserName = security.getSystemUserName(); * * @return the os user name */ public String js_getSystemUserName() { return System.getProperty("user.name"); //$NON-NLS-1$ } /** * @clonedesc js_getUserName(Object) * @sampleas js_getUserName(Object) * * @return the user name */ @JSFunction public String getUserName() throws ServoyException { return js_getUserName(null); } /** * Get the current user name (null if not logged in), finds the user name for given user UID if passed as parameter. * * @sample * //gets the current loggedIn username * var userName = security.getUserName(); * * @description-mc * returns the current logged in username of that is used when doing a sync. * * @sample-mc * var username = security.getUserName(); * if (username != null) { * // user is logged in * } * * @param userUID the user UID used to retrieve the name * * @return the user name */ public String js_getUserName(Object userUID) throws ServoyException { checkAuthorized(); try { if (userUID == null) { if (application.getClientInfo().getUserName() == null && application.getUserUID() != null) { String userName = application.getUserManager().getUserName(application.getClientID(), application.getUserUID()); if (userName != null && userName.length() != 0) { application.getClientInfo().setUserName(userName); } } return application.getClientInfo().getUserName(); } else { String user_uid = normalizeUID(userUID); // A user uid was specified; look up the user name. return application.getUserManager().getUserName(application.getClientID(), user_uid); } } catch (Exception e) { Debug.error(e); } return null; } /** * Check whatever the current user is part of the specified group * * @sample * //check whatever user is part of the Administrators group * if(security.isUserMemberOfGroup('Administrators', security.getUserUID('admin'))) * { * // do administration stuff * } * * @param groupName name of the group to check * * @return dataset with groupnames */ public boolean js_isUserMemberOfGroup(String groupName) throws ServoyException { return application.getUserUID() != null ? js_isUserMemberOfGroup(groupName, application.getUserUID()) : false; } /** * Check whatever the user specified as parameter is part of the specified group. * * @sample * //check whatever user is part of the Administrators group * if(security.isUserMemberOfGroup('Administrators', security.getUserUID('admin'))) * { * // do administration stuff * } * * @param groupName name of the group to check * @param userUID UID of the user to check * * @return dataset with groupnames */ public boolean js_isUserMemberOfGroup(String groupName, Object userUID) throws ServoyException { JSDataSet userGroups = js_getUserGroups(userUID); return userGroups != null ? Arrays.asList(userGroups.js_getColumnAsArray(2)).indexOf(groupName) > -1 : false; } /** * Get all the groups of the current user. * * @sample * //get all the users in the security settings (Returns a JSDataset) * var dsUsers = security.getUsers() * * //loop through each user to get their group * //The getValue call is (row,column) where column 1 == id and 2 == name * for(var i=1 ; i<=dsUsers.getMaxRowIndex() ; i++) * { * //print to the output debugger tab: "user: " and the username * application.output("user:" + dsUsers.getValue(i,2)); * * //set p to the user group for the current user * /** @type {JSDataSet} */ * var p = security.getUserGroups(dsUsers.getValue(i,1)); * * for(k=1;k<=p.getMaxRowIndex();k++) * { * //print to the output debugger tab: "group" and the group(s) * //the user belongs to * application.output("group: " + p.getValue(k,2)); * } * } * * @return dataset with groupnames */ public JSDataSet js_getUserGroups() throws ServoyException { JSDataSet groups = null; if (application.getUserUID() != null) { groups = js_getUserGroups(application.getUserUID()); } return (groups == null ? new JSDataSet(new ApplicationException(ServoyException.INCORRECT_LOGIN)) : groups); } /** * Get all the groups for given user UID. * * @sample * //get all the users in the security settings (Returns a JSDataset) * var dsUsers = security.getUsers() * * //loop through each user to get their group * //The getValue call is (row,column) where column 1 == id and 2 == name * for(var i=1 ; i<=dsUsers.getMaxRowIndex() ; i++) * { * //print to the output debugger tab: "user: " and the username * application.output("user:" + dsUsers.getValue(i,2)); * * //set p to the user group for the current user * /** @type {JSDataSet} */ * var p = security.getUserGroups(dsUsers.getValue(i,1)); * * for(k=1;k<=p.getMaxRowIndex();k++) * { * //print to the output debugger tab: "group" and the group(s) * //the user belongs to * application.output("group: " + p.getValue(k,2)); * } * } * * @param userUID to retrieve the user groups * * @return dataset with groupnames */ public JSDataSet js_getUserGroups(Object userUID) throws ServoyException { checkAuthorized(); if (userUID == null) return null; String n_userUID = normalizeUID(userUID); try { String[] groups = null; if (n_userUID.equals(application.getUserUID())) { groups = application.getClientInfo().getUserGroups(); } if (groups == null) { groups = application.getUserManager().getUserGroups(application.getClientID(), n_userUID.toString()); } if (groups != null) { List rows = new ArrayList(); for (String element : groups) { // fetch the id. int groupId = getGroupId(element); rows.add(new Object[] { new Integer(groupId), element }); } return new JSDataSet(application, new BufferedDataSet(new String[] { "group_id", "group_name" }, rows)); //$NON-NLS-1$ //$NON-NLS-2$ } } catch (Exception e) { Debug.error(e); } return new JSDataSet(); } /** * Set a new password for the given userUID. * Note: this method can only be called by an admin user or a normal logged in user changing its own password. * * @sample * if(security.checkPassword(security.getUserUID(), 'password1')) * { * security.setPassword(security.getUserUID(), 'password2') * } * else * { * application.output('wrong password') * } * * @param a_userUID the userUID to set the new password for * @param password the new password * * @return true if changed */ public boolean js_setPassword(Object a_userUID, String password) throws ServoyException { checkAuthorized(); if (a_userUID == null) return false; String userUID = normalizeUID(a_userUID); try { return application.getUserManager().setPassword(application.getClientID(), userUID.toString(), password, true); } catch (Exception e) { application.reportJSError("Can't change password of user " + a_userUID + ", only admin users can create/change security stuff", e); return false; } } /** * @deprecated As of release 3.0, replaced by {@link #setUserUID(Object,String)}. */ @Deprecated public void js_setUserId(Object userUID, String newUserUID) throws ServoyException { js_setUserUID(userUID, newUserUID); } /** * Set a new userUID for the given userUID. * Note: this method can only be called by an admin. * * @sampleas js_createGroup(String) * * @param a_userUID the userUID to set the new user UID for * @param newUserUID the new user UID * @return true if changed */ public boolean js_setUserUID(Object a_userUID, String newUserUID) throws ServoyException { checkAuthorized(); if (a_userUID == null) return false; String userUID = normalizeUID(a_userUID); try { return application.getUserManager().setUserUID(application.getClientID(), userUID.toString(), newUserUID); } catch (Exception e) { Debug.error(e); return false; } } /** * Returns true if the password for that userUID is correct, else false. * * @sample * if(security.checkPassword(security.getUserUID(), 'password1')) * { * security.setPassword(security.getUserUID(), 'password2') * } * else * { * application.output('wrong password') * } * * @param a_userUID the userUID to check the password for * @param password the new password * @return true if password oke */ public boolean js_checkPassword(Object a_userUID, String password) throws ServoyException { checkAuthorized(); if (a_userUID == null) return false; String userUID = normalizeUID(a_userUID); try { return application.getUserManager().checkPasswordForUserUID(application.getClientID(), userUID.toString(), password); } catch (Exception e) { Debug.error(e); } return false; } /** * Creates a group, returns the groupname (or null when group couldn't be created). * Note: this method can only be called by an admin. * * @sample * var deleteGroup = true; * //ceate a group * var groupName = security.createGroup('myGroup'); * if (groupName) * { * //create a user * var uid = security.createUser('myusername', 'mypassword'); * if (uid) //test if user was created * { * //set a newUID for the user * var isChanged = security.setUserUID(uid,'myUserUID') * // add user to group * security.addUserToGroup(uid, groupName); * // if not delete group, do delete group * if (deleteGroup) * { * security.deleteGroup(groupName); * } * } *} * * @param groupName the group name to create * * @return the created groupname */ public String js_createGroup(String groupName) throws ServoyException { checkAuthorized(); try { int groupId = application.getUserManager().createGroup(application.getClientID(), groupName); if (groupId != -1) return groupName; } catch (Exception e) { application.reportJSError("Can't create group: " + groupName + ", only admin users can create/change security stuff", e); } return null; } /** * @clonedesc js_createUser(String, String, Object) * @sampleas js_createUser(String, String, Object) * * @param username the username * @param password the user password * * @return the userUID the created userUID, will be same if provided */ public Object js_createUser(String username, String password) throws ServoyException { return js_createUser(username, password, null); } /** * Creates a new user, returns new uid (or null when group couldn't be created or user alreay exist). * Note: this method can only be called by an admin. * * @sample * var removeUser = true; * //create a user * var uid = security.createUser('myusername', 'mypassword'); * if (uid) //test if user was created * { * // Get all the groups * var set = security.getGroups(); * for(var p = 1 ; p <= set.getMaxRowIndex() ; p++) * { * // output name of the group * application.output(set.getValue(p, 2)); * // add user to group * security.addUserToGroup(uid, set.getValue(p,2)); * } * // if not remove user, remove user from all the groups * if(!removeUser) * { * // get now all the groups that that users has (all if above did go well) * var set =security.getUserGroups(uid); * for(var p = 1;p<=set.getMaxRowIndex();p++) * { * // output name of the group * application.output(set.getValue(p, 2)); * // remove the user from the group * security.removeUserFromGroup(uid, set.getValue(p,2)); * } * } * else * { * // delete the user (the user will be removed from the groups) * security.deleteUser(uid); * } * } * * @param username the username * @param password the user password * @param userUID the user UID to use * * @return the userUID the created userUID, will be same if provided */ public Object js_createUser(String username, String password, Object userUID) throws ServoyException { checkAuthorized(); if (username == null || username.length() == 0 || password == null || password.length() == 0) return null; try { // Check if the user name is free. int userId = application.getUserManager().getUserIdByUserName(application.getClientID(), username); if (userId != -1) return null; String user_uid = normalizeUID(userUID); // If the user uid is specified, check if the UID is free. if (user_uid != null) { String userName = application.getUserManager().getUserName(application.getClientID(), user_uid); if (userName != null) return null; } userId = application.getUserManager().createUser(application.getClientID(), username, password, user_uid, false); if (userId != -1) return application.getUserManager().getUserUID(application.getClientID(), userId); } catch (Exception e) { application.reportJSError("Can't create user: " + username + ", only admin users can create/change security stuff", e); } return null; } /** * Deletes an user. returns true if no error was reported. * Note: this method can only be called by an admin. * * @sampleas js_createUser(String, String, Object) * * @param userUID The UID of the user to be deleted. * * @return true if the user is successfully deleted. */ public boolean js_deleteUser(Object userUID) throws ServoyException { checkAuthorized(); if (userUID == null) return false; String n_userUID = normalizeUID(userUID); try { return application.getUserManager().deleteUser(application.getClientID(), n_userUID.toString()); } catch (Exception e) { application.reportJSError("Can't delete user: " + userUID + ", only admin users can create/change security stuff", e); } return false; } /** * Deletes a group, returns true if no error was reported. * Note: this method can only be called by an admin. * * @sampleas js_createGroup(String) * * @param groupName the name of the group to delete * @return true if deleted */ public boolean js_deleteGroup(Object groupName) throws ServoyException { checkAuthorized(); if (groupName == null) return false; try { if (groupName instanceof Number) { return application.getUserManager().deleteGroup(application.getClientID(), ((Number)groupName).intValue()); } else { int gid = getGroupId(groupName.toString()); return application.getUserManager().deleteGroup(application.getClientID(), gid); } } catch (Exception e) { application.reportJSError("Can't delete group: " + groupName + ", only admin users can create/change security stuff", e); } return false; } /** * Changes the username of the specified userUID. * Note: this method can only be called by an admin user or a normal logged in user changing its own userName. * * @sample * if(security.changeUserName(security.getUserUID('name1'), 'name2')) * { * application.output('Username changed'); * } * * @param a_userUID the userUID to work on * @param username the new username * @return true if changed */ public boolean js_changeUserName(Object a_userUID, String username) throws ServoyException { checkAuthorized(); if (a_userUID == null || username == null || username.length() == 0) return false; String userUID = normalizeUID(a_userUID); try { return application.getUserManager().changeUserName(application.getClientID(), userUID.toString(), username); } catch (Exception e) { application.reportJSError("Can't change username of " + a_userUID + ", only admin users can create/change security stuff", e); } return false; } /** * Changes the groupname of a group. * Note: this method can only be called by an admin. * * @sample security.changeGroupName('oldGroup', 'newGroup'); * * @param oldGroupName the old name * @param newGroupName the new name * @return true if changed */ public boolean js_changeGroupName(Object oldGroupName, String newGroupName) throws ServoyException { checkAuthorized(); if (oldGroupName == null || newGroupName == null || newGroupName.length() == 0 || newGroupName.equals(IRepository.ADMIN_GROUP) || oldGroupName.equals(IRepository.ADMIN_GROUP)) return false; try { if (oldGroupName instanceof Number) { return application.getUserManager().changeGroupName(application.getClientID(), ((Number)oldGroupName).intValue(), newGroupName); } else { return application.getUserManager().changeGroupName(application.getClientID(), getGroupId(oldGroupName.toString()), newGroupName); } } catch (Exception e) { application.reportJSError("Can't change groupname of " + oldGroupName + ", only admin users can create/change security stuff", e); } return false; } /** * @clonedesc js_getUsers(String) * @sampleas js_getUsers(String) * * @return dataset with all the users */ public JSDataSet js_getUsers() throws ServoyException { return js_getUsers(null); } /** * Get all the users in the security settings (returns a dataset). * * @sampleas js_getUserGroups(Object) * @param groupName the group to filter on * @return dataset with all the users */ public JSDataSet js_getUsers(String groupName) throws ServoyException { checkAuthorized(); try { IDataSet users = null; if (groupName != null && groupName.length() > 0) { users = application.getUserManager().getUsersByGroup(application.getClientID(), groupName); } else { users = application.getUserManager().getUsers(application.getClientID()); } return users == null ? new JSDataSet() : new JSDataSet(application, users); } catch (Exception e) { Debug.error(e); } return null; } /** * Get all the groups (returns a dataset). * first id column is deprecated!, use only the group name column. * * @sampleas js_createUser(String, String, Object) * @return dataset with all the groups */ public JSDataSet js_getGroups() throws ServoyException { checkAuthorized(); try { IDataSet groups = application.getUserManager().getGroups(application.getClientID()); return groups == null ? new JSDataSet() : new JSDataSet(application, groups); } catch (Exception e) { Debug.error(e); } return null; } /** * Adds an user to a named group. * Note: this method can only be called by an admin. * * @sample * var userUID = security.getUserUID(); * security.addUserToGroup(userUID, 'groupname'); * * @param a_userUID the user UID to be added * @param groupName the group to add to * @return true if added */ public boolean js_addUserToGroup(Object a_userUID, Object groupName) throws ServoyException { checkAuthorized(); if (a_userUID == null || groupName == null) return false; String userUID = normalizeUID(a_userUID); try { int userId = application.getUserManager().getUserIdByUID(application.getClientID(), userUID.toString()); if (userId != -1) { if (groupName instanceof Number) { return application.getUserManager().addUserToGroup(application.getClientID(), userId, ((Number)groupName).intValue()); } else { return application.getUserManager().addUserToGroup(application.getClientID(), userId, getGroupId(groupName.toString())); } } } catch (Exception e) { application.reportJSError("Can't add user " + a_userUID + " to group: " + groupName + ", only admin users can create/change security stuff", e); } return false; } /** * Removes an user from a group. * Note: this method can only be called by an admin. * * @sampleas js_createUser(String, String, Object) * * @param a_userUID the user UID to be removed * @param groupName the group to remove from * @return true if removed */ public boolean js_removeUserFromGroup(Object a_userUID, Object groupName) throws ServoyException { checkAuthorized(); if (a_userUID == null || groupName == null) return false; String userUID = normalizeUID(a_userUID); try { int userId = application.getUserManager().getUserIdByUID(application.getClientID(), userUID.toString()); if (userId != -1) { if (groupName instanceof Number) { return application.getUserManager().removeUserFromGroup(application.getClientID(), userId, ((Number)groupName).intValue()); } else { return application.getUserManager().removeUserFromGroup(application.getClientID(), userId, getGroupId(groupName.toString())); } } } catch (Exception e) { application.reportJSError("Can't remove user " + a_userUID + " from group " + groupName + ", only admin users can create/change security stuff", e); } return false; } /** * @clonedesc js_logout(String,String,Object) * @sampleas js_logout(String,String,Object) * */ @JSFunction public void logout() { js_logout(null, null, null); } /** * @clonedesc js_logout(String,String,Object) * @sampleas js_logout(String,String,Object) * * @param solutionToLoad the solution to load after logout */ public void js_logout(String solutionToLoad) { js_logout(solutionToLoad, null, null); } /** * @clonedesc js_logout(String,String,Object) * @sampleas js_logout(String,String,Object) * * @param solutionToLoad the solution to load after logout * @param method the method to run in the solution to load */ public void js_logout(String solutionToLoad, String method) { js_logout(solutionToLoad, method, null); } /** * Logout the current user and close the solution, if the solution requires authentication and user is logged in. * You can redirect to another solution if needed; if you want to go to a different url, you need to call application.showURL(url) before calling security.logout() (this is only applicable for Web Client). * An alternative option to close a solution and to open another solution, while keeping the user logged in, is application.closeSolution(). * * @sample * //Set the url to go to after logout. * //application.showURL('http://www.servoy.com', '_self'); //Web Client only * security.logout(); * //security.logout('solution_name');//log out and close current solution and open solution 'solution_name' * //security.logout('solution_name','global_method_name');//log out, close current solution, open solution 'solution_name' and call global method 'global_method_name' of the newly opened solution * //security.logout('solution_name','global_method_name','my_string_argument');//log out, close current solution, open solution 'solution_name', call global method 'global_method_name' with argument 'my_argument' * //security.logout('solution_name','global_second_method_name',2); * //Note: specifying a solution will not work in the Developer due to debugger dependencies * //specified solution should be of compatible type with client (normal type or client specific(Smart client only/Web client only) type ) * * @description-mc * Clears the current credentials that the user specified when doing a sync. When the next sync happens the login form will be shown. * * @sample-mc * security.logout(); * plugins.mobile.sync(); * * @param solutionToLoad the solution to load after logout * @param method the method to run in the solution to load * @param argument the argument to pass to the method to run */ public void js_logout(String solutionToLoad, String method, Object argument) { application.logout(new Object[] { solutionToLoad, method, argument }); } /** * Returns a boolean value for security rights. * * @sampleas js_canUpdate(String) * * @param dataSource the datasource * @return true if allowed */ public boolean js_canDelete(String dataSource) { return hasAccess(dataSource, IRepository.DELETE); } /** * @param serverName the server name * @param tableName the table name * * @deprecated replaced by canDelete(String) */ @Deprecated public boolean js_canDelete(Object serverName, Object tableName) { if (serverName != null && tableName != null) { return js_canDelete(DataSourceUtils.createDBTableDataSource(serverName.toString(), tableName.toString())); } return false; } /** * Returns a boolean value for security rights. * * @sample * var dataSource = controller.getDataSource(); * var canDelete = security.canDelete(dataSource); * var canInsert = security.canInsert(dataSource); * var canUpdate = security.canUpdate(dataSource); * var canRead = security.canRead(dataSource); * application.output("Can delete? " + canDelete); * application.output("Can insert? " + canInsert); * application.output("Can update? " + canUpdate); * application.output("Can read? " + canRead); * * @param dataSource the datasource * @return true if allowed */ public boolean js_canUpdate(String dataSource) { return hasAccess(dataSource, IRepository.UPDATE); } /** * @param serverName the server name * @param tableName the parameter name * * @deprecated replaced by canUpdate(String) */ @Deprecated public boolean js_canUpdate(Object serverName, Object tableName) { if (serverName != null && tableName != null) { return js_canUpdate(DataSourceUtils.createDBTableDataSource(serverName.toString(), tableName.toString())); } return false; } /** * Returns a boolean value for security rights. * * @sampleas js_canUpdate(String) * * @param dataSource the datasource * @return true if allowed */ public boolean js_canInsert(String dataSource) { return hasAccess(dataSource, IRepository.INSERT); } /** * @param serverName the server name * @param tableName the table name * * @deprecated replaced by canInsert(String) */ @Deprecated public boolean js_canInsert(Object serverName, Object tableName) { if (serverName != null && tableName != null) { return js_canInsert(DataSourceUtils.createDBTableDataSource(serverName.toString(), tableName.toString())); } return false; } /** * Returns a boolean value for security rights. * * @sampleas js_canUpdate(String) * * @param dataSource the datasource * @return true if allowed */ public boolean js_canRead(String dataSource) { return hasAccess(dataSource, IRepository.READ); } /** * @param serverName the server name * @param tableName the table name * * @deprecated replaced by canRead(String) */ @Deprecated public boolean js_canRead(Object serverName, Object tableName) { if (serverName != null && tableName != null) { return js_canRead(DataSourceUtils.createDBTableDataSource(serverName.toString(), tableName.toString())); } return false; } private boolean hasAccess(String dataSource, int accessType) { try { Table table = (Table)application.getFoundSetManager().getTable(dataSource); if (table != null) { return application.getFoundSetManager().getEditRecordList().hasAccess(table, accessType); } } catch (RepositoryException ex) { Debug.error(ex); } return false; } //made easier for developers to work with old ids private String normalizeUID(Object a_userUID) { String userUID = null; if (a_userUID instanceof Number) { userUID = "" + ((Number)a_userUID).longValue(); //prevent '23.0' formatting for numbers (the old ids where numbers) //$NON-NLS-1$ } else if (a_userUID != null) { userUID = a_userUID.toString(); } return userUID; } /** * Login to be able to leave the solution loginForm. * * Example: Group names may be received from LDAP (Lightweight Directory Access Protocol) - a standard protocol used in web browsers and email applications to enable lookup queries that access a directory listing. * * @sample * var groups = ['Administrators']; //normally these groups are for example received from LDAP * var user_uid = scopes.globals.email; //also this uid might be received from external authentication method * var ok = security.login(scopes.globals.username, user_uid , groups) * if (!ok) * { * plugins.dialogs.showErrorDialog('Login failure', 'Already logged in? or no user_uid/groups specified?', 'OK') * } * * @param username the username, like 'JamesWebb' * @param a_userUID the user UID to process login for * @param groups the groups array * @return true if loggedin */ public boolean js_login(String username, Object a_userUID, String[] groups) { if (application.getUserManager() == null) { // cannot login locally in client because client has to be authenticated at the server first return false; } String userUID = normalizeUID(a_userUID); if (groups == null || groups.length == 0 || username == null || username.length() == 0 || userUID == null || userUID.length() == 0) return false; // check if the groups all exist IDataSet groupsDataSet; try { groupsDataSet = application.getUserManager().getGroups(application.getClientID()); } catch (Exception e) { Debug.error(e); return false; } for (int g = 0; g < groups.length; g++) { int i; for (i = 0; i < groupsDataSet.getRowCount() && !groupsDataSet.getRow(i)[1].equals(groups[g]); i++) { } if (i == groupsDataSet.getRowCount()) { Debug.log("Could not log in user for unknown group '" + groups[g] + "'"); //$NON-NLS-1$//$NON-NLS-2$ return false; } } ClientInfo ci = application.getClientInfo(); if (ci.getUserUid() != null && !ci.getUserUid().equalsIgnoreCase(userUID)) { // already logged in return false; } ci.setUserName(username); ci.setUserUid(userUID); ci.setUserGroups(groups); if (application.getSolution().getSolutionType() != SolutionMetaData.AUTHENTICATOR) { application.clearLoginForm(); } return true; } private void checkAuthorized() throws ServoyException { if (!application.haveRepositoryAccess()) { // no access to repository yet, have to log in first throw new ServoyException(ServoyException.CLIENT_NOT_AUTHORIZED); } } /** * Authenticate the given credentials against the mobile service solution. * It will set the credentials and then do a sync call to the server. * * @sample * // method will return null in mobile client, the same flow as for default login page will happen after calling this method * security.authenticate(['myusername', 'mypassword']); * * @param credentials array whose elements are passed as arguments to the authenticator method, in case of servoy built-in authentication this should be [username, password] * * @return authentication result from authenticator solution or boolean in case of servoy built-in authentication */ @JSFunction public Object authenticate(Object[] credentials) { return authenticate(null, null, credentials); } /** * Authenticate to the Servoy Server using one of the installed authenticators or the Servoy default authenticator. * * Note: this method should be called from a login solution, once logged in, the authenticate method has no effect. * * @sample * // create the credentials object as expected by the authenticator solution * var ok = security.authenticate('myldap_authenticator', 'login', [scopes.globals.userName, scopes.globals.passWord]) * if (!ok) * { * plugins.dialogs.showErrorDialog('Login failed', 'OK') * } * * // if no authenticator name is used, the credentials are checked using the Servoy built-in user management * ok = security.authenticate(null, null, [scopes.globals.userName, scopes.globals.passWord]) * * @description-mc * Authenticate the given credentials against the mobile service solution. First two parameters are not used in mobile solution, just the credentials. * It will set the credentials and then do a sync call to the server. * * @sample-mc * // method will return null in mobile client, the same flow as for default login page will happen after calling this method * security.authenticate(null, null, ['myusername', 'mypassword']); * * @param authenticator_solution authenticator solution installed on the Servoy Server, null for servoy built-in authentication * @param method authenticator method, null for servoy built-in authentication * @param credentials array whose elements are passed as arguments to the authenticator method, in case of servoy built-in authentication this should be [username, password] * * @return authentication result from authenticator solution or boolean in case of servoy built-in authentication */ @JSFunction public Object authenticate(String authenticator_solution, String method, Object[] credentials) { try { return application.authenticate(authenticator_solution, method, credentials); } catch (ServoyException e) { Debug.error(e); return null; } } /** * Authenticate to the Servoy Server using one of the installed authenticators or the Servoy default authenticator. * * Note: this method should be called from a login solution. * * @sample * // create the credentials object as expected by the authenticator solution * var ok = security.authenticate('myldap_authenticator', 'login', [scopes.globals.userName, scopes.globals.passWord]) * if (!ok) * { * plugins.dialogs.showErrorDialog('Login failed', 'OK') * } * * // if no authenticator name is used, the credentials are checked using the Servoy built-in user management * ok = security.authenticate(null, null, [scopes.globals.userName, scopes.globals.passWord]) * * @param authenticator_solution authenticator solution installed on the Servoy Server, null for servoy built-in authentication * @param method authenticator method, null for servoy built-in authentication * * @return authentication result from authenticator solution or boolean in case of servoy built-in authentication */ public Object js_authenticate(String authenticator_solution, String method) { return authenticate(authenticator_solution, method, null); } /** * Sets the security settings; the entries contained in the given dataset will override those contained in the current security settings. * * NOTE: The security.getElementUUIDs and security.setSecuritySettings functions can be used to define custom security that overrides Servoy security. * For additional information see the function security.getElementUUIDs. * * @sample * var colNames = new Array(); * colNames[0] = 'uuid'; * colNames[1] = 'flags'; * var dataset = databaseManager.createEmptyDataSet(0,colNames); * * var row = new Array(); * row[0] = '413a4d69-becb-4ae4-8fdd-980755d6a7fb';//normally retreived via security.getElementUUIDs(...) * row[1] = JSSecurity.VIEWABLE|JSSecurity.ACCESSIBLE; // use bitwise 'or' for both * dataset.addRow(row);//setting element security * * row = new Array(); * row[0] = 'example_data.orders'; * row[1] = JSSecurity.READ|JSSecurity.INSERT|JSSecurity.UPDATE|JSSecurity.DELETE|JSSecurity.TRACKING; //use bitwise 'or' for multiple flags * dataset.addRow(row);//setting table security * * security.setSecuritySettings(dataset);//to be called in solution startup method * * @param dataset the dataset with security settings */ public void js_setSecuritySettings(Object dataset) // uuid/server.tablename , integer(flags) { if (dataset instanceof JSDataSet) { dataset = ((JSDataSet)dataset).getDataSet(); } if (dataset instanceof IDataSet) { application.getFoundSetManager().getEditRecordList().clearSecuritySettings(); Map<Object, Integer> sp = new HashMap<Object, Integer>(); IDataSet ds = (IDataSet)dataset; if (ds.getColumnCount() < 2) return; for (int i = 0; i < ds.getRowCount(); i++) { Object[] row = ds.getRow(i); if (row[0] != null && row[1] != null) { Integer val = new Integer(Utils.getAsInteger(row[1])); try { boolean matched = false; if (row[0] instanceof UUID) { sp.put(row[0], val); matched = true; } else if (row[0].toString().indexOf('-') > 0) { UUID uuid = UUID.fromString(row[0].toString()); sp.put(uuid, val); matched = true; } else { String datasource = row[0].toString(); if (datasource.indexOf('.') != -1) { String[] server_table = datasource.split("\\."); if (server_table.length == 2) { IServer server = application.getSolution().getServer(server_table[0]); if (server != null) { ITable table = server.getTable(server_table[1]); if (table != null) { Iterator<String> it = table.getRowIdentColumnNames(); if (it.hasNext()) { sp.put(Utils.getDotQualitfied(table.getServerName(), table.getName(), it.next()), val); matched = true; } } } } } } if (!matched) { Debug.error("security.setSecuritySettings: could not apply security settings for '" + row[0] + "'"); } } catch (Exception e) { Debug.error(e); } } } application.getFlattenedSolution().overrideSecurityAccess(sp); } } /** * Returns the form elements UUID's as dataset, the one with no name is the form itself. * * @sample var formElementsUUIDDataSet = security.getElementUUIDs('orders_form'); * * @param formname the formname to retieve the dataset for * @return dataset with element info */ public JSDataSet js_getElementUUIDs(String formname)// return dataset with name, uuid (note: null name is form uuid) { Form f = application.getFlattenedSolution().getForm(formname); if (f == null) f = application.getFormManager().getPossibleForm(formname); if (f != null) { List elements = new ArrayList(); elements.add(new Object[] { null, f.getUUID() }); Iterator it = f.getAllObjects(); while (it.hasNext()) { IPersist elem = (IPersist)it.next(); int type = elem.getTypeID(); if (type == IRepository.GRAPHICALCOMPONENTS || type == IRepository.FIELDS || type == IRepository.PORTALS || type == IRepository.RECTSHAPES || type == IRepository.SHAPES || type == IRepository.BEANS || type == IRepository.TABPANELS) { if (elem instanceof ISupportName && ((ISupportName)elem).getName() != null) { elements.add(new Object[] { ((ISupportName)elem).getName(), elem.getUUID() }); } } } IDataSet set = new BufferedDataSet(new String[] { "name", "uuid" }, elements); return new JSDataSet(application, set); } return new JSDataSet(application); } public Class< ? >[] getAllReturnedTypes() { return new Class[] { TABLESECURITY.class, FORMSECURITY.class }; } /** * @see java.lang.Object#toString() */ @Override public String toString() { return "security"; //$NON-NLS-1$ } public void destroy() { application = null; } }