/* Copyright (c) 2001-2017, The HSQL Development Group
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the HSQL Development Group nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hsqldb.rights;
import org.hsqldb.
Database;
import org.hsqldb.
HsqlException;
import org.hsqldb.
HsqlNameManager.
HsqlName;
import org.hsqldb.
Routine;
import org.hsqldb.
Schema;
import org.hsqldb.
SchemaObject;
import org.hsqldb.
Session;
import org.hsqldb.
SqlInvariants;
import org.hsqldb.
Tokens;
import org.hsqldb.error.
Error;
import org.hsqldb.error.
ErrorCode;
import org.hsqldb.lib.
HashMappedList;
import org.hsqldb.lib.
HsqlArrayList;
import org.hsqldb.result.
Result;
/**
* Manages the User objects for a Database instance.
* The special users PUBLIC_USER_NAME and SYSTEM_AUTHORIZATION_NAME
* are created and managed here. SYSTEM_AUTHORIZATION_NAME is also
* special in that the name is not kept in the user "list"
* (PUBLIC_USER_NAME is kept in the list because it's needed by MetaData
* routines via "listVisibleUsers(x, true)").
*
* @author Campbell Burnet (campbell-burnet@users dot sourceforge.net)
* @author Fred Toussi (fredt@users dot sourceforge.net)
*
* @version 2.3.0
* @since 1.7.2
* @see User
*/
public final class
UserManager {
/**
* This object's set of User objects. <p>
*
* Note: The special _SYSTEM role
* is not included in this list but the special PUBLIC
* User object is kept in the list because it's needed by MetaData
* routines via "listVisibleUsers(x, true)".
*/
private
HashMappedList userList;
private
GranteeManager granteeManager;
/**
* The function for password complexity.
*/
Routine pwCheckFunction;
Routine extAuthenticationFunction;
/**
* Construction happens once for each Database instance.
*
* Creates special users PUBLIC_USER_NAME and SYSTEM_AUTHORIZATION_NAME.
* Sets up association with the GranteeManager for this database.
*/
public
UserManager(
Database database) {
granteeManager =
database.
getGranteeManager();
userList = new
HashMappedList();
}
/**
* Creates a new User object under management of this object. <p>
*
* A set of constraints regarding user creation is imposed: <p>
*
* <OL>
* <LI>If the specified name is null, then an
* ASSERTION_FAILED exception is thrown stating that
* the name is null.
*
* <LI>If this object's collection already contains an element whose
* name attribute equals the name argument, then
* a GRANTEE_ALREADY_EXISTS exception is thrown.
* (This will catch attempts to create Reserved grantee names).
* </OL>
*/
public
User createUser(
Session session,
HsqlName name,
String password,
boolean
isDigest) {
// This will throw an appropriate exception if grantee already exists,
// regardless of whether the name is in any User, Role, etc. list.
User user =
granteeManager.
addUser(
name);
if (
session == null) {
user.
setPassword(
password,
isDigest);
} else {
try {
setPassword(
session,
user,
password,
isDigest);
} catch (
HsqlException e) {
granteeManager.
removeNewUser(
name);
throw
e;
}
}
// this cannot fail
userList.
add(
name.
name,
user);
return
user;
}
public void
setPassword(
Session session,
User user,
String password,
boolean
isDigest) {
if (!
isDigest && !
checkComplexity(
session,
password)) {
throw
Error.
error(
ErrorCode.
PASSWORD_COMPLEXITY);
}
// requires: UserManager.createSAUser(), UserManager.createPublicUser()
user.
setPassword(
password,
isDigest);
}
public boolean
checkComplexity(
Session session,
String password) {
if (
session == null ||
pwCheckFunction == null) {
return true;
}
Result result =
pwCheckFunction.
invoke(
session,
new
Object[]{
password }, null,
true);
Boolean check = (
Boolean)
result.
getValueObject();
if (
check == null || !
check.
booleanValue()) {
return false;
}
return true;
}
/**
* Attempts to drop a User object with the specified name
* from this object's set. <p>
*
* A successful drop action consists of: <p>
*
* <UL>
*
* <LI>removing the User object with the specified name
* from the set.
*
* <LI>revoking all rights from the removed User<br>
* (this ensures that in case there are still references to the
* just dropped User object, those references
* cannot be used to erroneously access database objects).
*
* </UL> <p>
*
*/
public void
dropUser(
String name) {
boolean
reservedUser =
GranteeManager.
isReserved(
name);
if (
reservedUser) {
throw
Error.
error(
ErrorCode.
X_28502,
name);
}
boolean
result =
granteeManager.
removeGrantee(
name);
if (!
result) {
throw
Error.
error(
ErrorCode.
X_28501,
name);
}
User user = (
User)
userList.
remove(
name);
if (
user == null) {
throw
Error.
error(
ErrorCode.
X_28501,
name);
}
}
public void
createFirstUser(
String username,
String password) {
boolean
isQuoted = true;
if (
username.
equalsIgnoreCase("SA")) {
username = "SA";
isQuoted = false;
}
HsqlName name =
granteeManager.
database.
nameManager.
newHsqlName(
username,
isQuoted,
SchemaObject.
GRANTEE);
User user =
createUser(null,
name,
password, false);
user.
isLocalOnly = true;
granteeManager.
grant(
name.
name,
SqlInvariants.
DBA_ADMIN_ROLE_NAME,
granteeManager.
getDBARole());
}
/**
* Returns the User object with the specified name and
* password from this object's set.
*/
public
User getUser(
String name,
String password) {
if (
name == null) {
name = "";
}
if (
password == null) {
password = "";
}
User user = (
User)
userList.
get(
name);
boolean
isLocal =
user != null &&
user.
isLocalOnly;
if (
extAuthenticationFunction == null ||
isLocal) {
user =
get(
name);
user.
checkPassword(
password);
return
user;
}
/*
* Authentication returns String[]. When null, use the existing
* user object only, with existing privileges.
* When not null, ignore if user exists. Otherwise create a user and
* assign the list of roles to the user.
*/
Result result =
extAuthenticationFunction.
invokeJavaMethodDirect(new
String[] {
granteeManager.
database.
getNameString(),
name,
password
});
if (
result.
isError()) {
throw
Error.
error(
ErrorCode.
X_28501,
result.
getMainString());
}
Object[]
roles = (
Object[])
result.
getValueObject();
if (
user == null) {
HsqlName hsqlName =
granteeManager.
database.
nameManager.
newHsqlName(
name, true,
SchemaObject.
GRANTEE);
user =
createUser(null,
hsqlName, "", false);
user.
isExternalOnly = true;
}
if (
roles == null) {
user.
updateAllRights();
return
user;
}
// this clears all existing privileges of the user
user.
clearPrivileges();
// assigns the roles to the user
for (int
i = 0;
i <
roles.length;
i++) {
try {
Grantee role =
granteeManager.
getRole((
String)
roles[
i]);
user.
grant(
role);
} catch (
HsqlException e) {}
}
user.
updateAllRights();
for (int
i = 0;
i <
roles.length;
i++) {
Schema schema =
granteeManager.
database.
schemaManager.
findSchema(
(
String)
roles[
i]);
if (
schema != null) {
user.
setInitialSchema(
schema.
getName());
break;
}
}
return
user;
}
/**
* Retrieves this object's set of User objects as
* an associative list.
*/
public
HashMappedList getUsers() {
return
userList;
}
public boolean
exists(
String name) {
return
userList.
get(
name) == null ? false
: true;
}
/**
* Returns the User object identified by the
* name argument.
*/
public
User get(
String name) {
User user = (
User)
userList.
get(
name);
if (
user == null) {
throw
Error.
error(
ErrorCode.
X_28501,
name);
}
return
user;
}
/**
* Retrieves the <code>User</code> objects representing the database
* users that are visible to the <code>User</code> object
* represented by the <code>session</code> argument. <p>
*
* If the <code>session</code> argument's <code>User</code> object
* attribute has isAdmin() true (directly or by virtue of a Role),
* then all of the
* <code>User</code> objects in this collection are considered visible.
* Otherwise, only this object's special <code>PUBLIC</code>
* <code>User</code> object attribute and the session <code>User</code>
* object, if it exists in this collection, are considered visible. <p>
*
* @param session The <code>Session</code> object used to determine
* visibility
* @return a list of <code>User</code> objects visible to
* the <code>User</code> object contained by the
* <code>session</code> argument.
*
*/
public
HsqlArrayList listVisibleUsers(
Session session) {
HsqlArrayList list;
User user;
boolean
isAdmin;
String sessionName;
String userName;
list = new
HsqlArrayList();
isAdmin =
session.
isAdmin();
sessionName =
session.
getUsername();
if (
userList == null ||
userList.
size() == 0) {
return
list;
}
for (int
i = 0;
i <
userList.
size();
i++) {
user = (
User)
userList.
get(
i);
if (
user == null) {
continue;
}
userName =
user.
getName().
getNameString();
if (
isAdmin) {
list.
add(
user);
} else if (
sessionName.
equals(
userName)) {
list.
add(
user);
}
}
return
list;
}
/**
* Returns the specially constructed
* <code>SYSTEM_AUTHORIZATION_NAME</code>
* <code>User</code> object for the current <code>Database</code> object.
*
* @return the <code>SYS_AUTHORIZATION_NAME</code>
* <code>User</code> object
*
*/
public
User getSysUser() {
return
GranteeManager.
systemAuthorisation;
}
public synchronized void
removeSchemaReference(
String schemaName) {
for (int
i = 0;
i <
userList.
size();
i++) {
User user = (
User)
userList.
get(
i);
HsqlName schema =
user.
getInitialSchema();
if (
schema == null) {
continue;
}
if (
schemaName.
equals(
schema.
name)) {
user.
setInitialSchema(null);
}
}
}
public void
setPasswordCheckFunction(
Routine function) {
pwCheckFunction =
function;
}
public void
setExtAuthenticationFunction(
Routine function) {
extAuthenticationFunction =
function;
}
public
String[]
getInitialSchemaSQL() {
HsqlArrayList list = new
HsqlArrayList(
userList.
size());
for (int
i = 0;
i <
userList.
size();
i++) {
User user = (
User)
userList.
get(
i);
if (
user.
isSystem) {
continue;
}
HsqlName name =
user.
getInitialSchema();
if (
name == null) {
continue;
}
list.
add(
user.
getInitialSchemaSQL());
}
String[]
array = new
String[
list.
size()];
list.
toArray(
array);
return
array;
}
public
String[]
getAuthenticationSQL() {
HsqlArrayList list = new
HsqlArrayList();
String[]
array;
if (
pwCheckFunction != null) {
StringBuffer sb = new
StringBuffer();
sb.
append(
Tokens.
T_SET).
append(' ').
append(
Tokens.
T_DATABASE);
sb.
append(' ').
append(
Tokens.
T_PASSWORD).
append(' ');
sb.
append(
Tokens.
T_CHECK).
append(' ').
append(
Tokens.
T_FUNCTION);
sb.
append(' ');
sb.
append(
pwCheckFunction.
getSQLBodyDefinition());
list.
add(
sb.
toString());
}
if (
extAuthenticationFunction != null) {
StringBuffer sb = new
StringBuffer();
sb.
append(
Tokens.
T_SET).
append(' ').
append(
Tokens.
T_DATABASE);
sb.
append(' ').
append(
Tokens.
T_AUTHENTICATION).
append(' ');
sb.
append(
Tokens.
T_FUNCTION).
append(' ');
sb.
append(
extAuthenticationFunction.
getSQLBodyDefinition());
list.
add(
sb.
toString());
}
array = new
String[
list.
size()];
list.
toArray(
array);
return
array;
}
}