Juan Hernandez has uploaded a new change for review. Change subject: [WIP] Local authentication ......................................................................
[WIP] Local authentication This patch adds an authentication provider that uses the local users and groups database. It will appear in the system as a new "local" authentication domain. Access to the user database is implemented using the libc name service switch functions, so it will work with the local database stored in the /etc/passwd and /etc/group files as well as with external databases like NIS or LDAP. The authentication is implemented using SSH, so when the user types its user name and password the engine will try to open a SSH connection to localhost using that user name and password. It that succeeds the user is allowed to log in to the webadmin or user portal. The complete description of this feature is available here: http://www.ovirt.org/Features/Local_Authentication Change-Id: Ia418368dc8959e3e176a7f252fcc91fb682045ff Signed-off-by: Juan Hernandez <juan.hernan...@redhat.com> --- M backend/manager/modules/bll/pom.xml M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapBrokerUtils.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapFactory.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryData.java M backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryDataImpl.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalAuthenticateUserCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerCommandBase.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerImpl.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdGroupByGroupIdCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdListCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserNameCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGroup.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchGroupsByQueryCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchUserByQueryCommand.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUser.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserAuthenticator.java A backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserDatabase.java M backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java M packaging/fedora/spec/ovirt-engine.spec.in M pom.xml 22 files changed, 987 insertions(+), 1 deletion(-) git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/63/11863/1 diff --git a/backend/manager/modules/bll/pom.xml b/backend/manager/modules/bll/pom.xml index c1d01ef..94933c3 100644 --- a/backend/manager/modules/bll/pom.xml +++ b/backend/manager/modules/bll/pom.xml @@ -167,7 +167,24 @@ <artifactId>jboss-ejb-api_3.1_spec</artifactId> <version>${javax.ejb.api.version}</version> </dependency> + + <dependency> + <groupId>org.apache.mina</groupId> + <artifactId>mina-core</artifactId> + <version>${mina-core.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sshd</groupId> + <artifactId>sshd-core</artifactId> + <version>${sshd-core.version}</version> + </dependency> + + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna</artifactId> + </dependency> + </dependencies> <build> diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java index d5a1d74..6530447 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/SearchQuery.java @@ -196,6 +196,18 @@ ldapQueryData.setDomain(data.getDomain()); ldapQueryData.setFilterParameters(new Object[] { data.getQueryForAdBroker() }); + // Get the raw text as typed by the user (this should probably be + // changed, the front end should pass the raw text typed by the user + // directly to avoid this error prone parsing): + String pattern = getParameters().getSearchPattern(); + if (pattern.startsWith(SearchObjects.AD_USER_OBJ_NAME) || pattern.startsWith(SearchObjects.AD_GROUP_OBJ_NAME)) { + int index = pattern.indexOf("="); + if (index != -1) { + String text = pattern.substring(index + 1); + ldapQueryData.setQueryText(text); + } + } + @SuppressWarnings("unchecked") List<T> result = (List<T>) getLdapFactory(data.getDomain()) .RunAdAction(adActionType, diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapBrokerUtils.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapBrokerUtils.java index ea0c86d..b5b553c 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapBrokerUtils.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapBrokerUtils.java @@ -55,6 +55,7 @@ if (!filterInternalDomain) { results.add(Config.<String> GetValue(ConfigValues.AdminDomain).trim()); } + results.add(Config.<String> GetValue(ConfigValues.LocalDomain).trim()); return results; } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapFactory.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapFactory.java index 88a3027..6cdce41 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapFactory.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapFactory.java @@ -8,16 +8,22 @@ private static LdapBroker internalInstance; private static LdapBroker ldapInstance; + private static LdapBroker localInstance; + private static String internalDomain = Config.<String> GetValue(ConfigValues.AdminDomain).trim(); + private static String localDomain = Config.<String> GetValue(ConfigValues.LocalDomain).trim(); static { internalInstance = new InternalBrokerImpl(); ldapInstance = new LdapBrokerImpl(); + localInstance = new LocalBrokerImpl(); } public static LdapBroker getInstance(String domain) { if (domain.equalsIgnoreCase(internalDomain)) { return internalInstance; + } else if (domain.equalsIgnoreCase(localDomain)) { + return localInstance; } else { return ldapInstance; } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryData.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryData.java index a9acaf1..a8673f6 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryData.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryData.java @@ -18,4 +18,7 @@ public void setDomain(String domain); + public String getQueryText(); + + public void setQueryText(String text); } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryDataImpl.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryDataImpl.java index 502d142..29c64a1 100644 --- a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryDataImpl.java +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LdapQueryDataImpl.java @@ -6,6 +6,7 @@ private Object[] filterParameters; private Object[] baseDNParameters; private String domain; + private String text; @Override public LdapQueryType getLdapQueryType() { @@ -47,4 +48,13 @@ this.domain = domain; } + @Override + public String getQueryText() { + return text; + } + + @Override + public void setQueryText(String text) { + this.text = text; + } } diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalAuthenticateUserCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalAuthenticateUserCommand.java new file mode 100644 index 0000000..e911645 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalAuthenticateUserCommand.java @@ -0,0 +1,39 @@ +package org.ovirt.engine.core.bll.adbroker; + +import org.ovirt.engine.core.common.businessentities.LdapUser; +import org.ovirt.engine.core.dal.VdcBllMessages; + +public class LocalAuthenticateUserCommand extends LocalBrokerCommandBase { + public LocalAuthenticateUserCommand(LdapUserPasswordBaseParameters parameters) { + super(parameters); + } + + @Override + protected void executeQuery() { + // Make sure that the user exists in the database: + final String login = getParameters().getLoginName(); + final LocalUser local = getDatabase().getUserByName(login); + if (local == null) { + final UserAuthenticationResult result = new UserAuthenticationResult(VdcBllMessages.USER_FAILED_TO_AUTHENTICATE); + setReturnValue(result); + setSucceeded(false); + return; + } + + // Perform validation of credentials: + final String password = getParameters().getPassword(); + final boolean authenticated = getAuthenticator().authenticate(login, password); + if (!authenticated) { + final UserAuthenticationResult result = new UserAuthenticationResult(VdcBllMessages.USER_FAILED_TO_AUTHENTICATE); + setReturnValue(result); + setSucceeded(false); + return; + } + + // Fine, the user has been authenticated successfully: + final LdapUser user = mapUser(local); + final UserAuthenticationResult result = new UserAuthenticationResult(user); + setReturnValue(result); + setSucceeded(true); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerCommandBase.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerCommandBase.java new file mode 100644 index 0000000..766887b --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerCommandBase.java @@ -0,0 +1,143 @@ +package org.ovirt.engine.core.bll.adbroker; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import org.ovirt.engine.core.common.businessentities.LdapGroup; +import org.ovirt.engine.core.common.businessentities.LdapUser; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public abstract class LocalBrokerCommandBase extends BrokerCommandBase { + // The log: + private static final Log log = LogFactory.getLog(LocalBrokerCommandBase.class); + + // Fake domain name: + protected static final String DOMAIN = "local"; + + // The local authenticator and database: + private static final LocalUserAuthenticator AUTHENTICATOR = new LocalUserAuthenticator(); + private static final LocalUserDatabase DATABASE = new LocalUserDatabase(); + + // Prefixes used to build map local user and group ids to UUIDS: + private static long UID_PREFIX = 0xaaa0000000000000l; + private static long GID_PREFIX = 0xbbb0000000000000l; + + protected LocalUserAuthenticator getAuthenticator() { + return AUTHENTICATOR; + } + + protected LocalUserDatabase getDatabase() { + return DATABASE; + } + + protected static Guid uid2uuid(final long uid) { + final UUID uuid = new UUID(UID_PREFIX, uid); + return new Guid(uuid); + } + + protected static int guid2uid(final Guid guid) { + final UUID uuid = guid.getUuid(); + if (uuid.getMostSignificantBits() == UID_PREFIX) { + return (int) uuid.getLeastSignificantBits(); + } + else { + throw new RuntimeException("The UUID \"" + uuid + "\" doesn't contain a valid user id."); + } + } + + protected static int guid2gid(final Guid guid) { + final UUID uuid = guid.getUuid(); + if (uuid.getMostSignificantBits() == GID_PREFIX) { + return (int) uuid.getLeastSignificantBits(); + } + else { + throw new RuntimeException("The UUID \"" + uuid + "\" doesn't contain a valid group id."); + } + } + + protected static Guid gid2uuid(final long gid) { + final UUID uuid = new UUID(GID_PREFIX, gid); + return new Guid(uuid); + } + + protected LocalBrokerCommandBase(final LdapBrokerBaseParameters parameters) { + super(parameters); + } + + public String getPROTOCOL() { + return "Local"; + } + + @Override + public LdapReturnValueBase execute() { + try { + executeQuery(); + } + catch (RuntimeException exception) { + log.error("Error in executing local user broker command.", exception); + _ldapReturnValue.setSucceeded(false); + _ldapReturnValue.setReturnValue(null); + } + return _ldapReturnValue; + } + + protected static LdapUser mapUser(final LocalUser local) { + // Create an empty user and set the basic attributes: + final LdapUser user = new LdapUser(); + user.setDomainControler(DOMAIN); + user.setUserName(local.getName()); + user.setUserId(uid2uuid(local.getUid())); + + // Get the rest of the attributes from the GECOS field, at the moment we + // assume that it contains the name of the user, the department, and the + // e-mail address separated by commas: + final String[] gecosFields = local.getGecos().split(","); + if (gecosFields.length > 0) { + final String[] gecosNameParts = gecosFields[0].split(" "); + if (gecosNameParts.length == 0) { + user.setName(local.getName()); + } + else { + if (gecosNameParts.length > 0) { + user.setName(gecosNameParts[0]); + } + if (gecosNameParts.length > 1) { + user.setSurName(gecosNameParts[1]); + } + } + } + if (gecosFields.length > 1) { + user.setDepartment(gecosFields[1]); + } + if (gecosFields.length > 2) { + user.setEmail(gecosFields[2]); + } + + // Get the list of groups: + final List<LocalGroup> groups = DATABASE.getGroupsByUserName(local.getName()); + final List<String> names = new ArrayList<String>(groups.size()); + for (LocalGroup group : groups) { + names.add(group.getName()); + } + user.setMemberof(names); + + // Return the mapped user: + return user; + } + + protected static LdapGroup mapGroup(final LocalGroup local) { + // Create an empty group and set the basic attributes: + final LdapGroup group = new LdapGroup(); + group.setdomain(DOMAIN); + group.setname(local.getName()); + group.setid(gid2uuid(local.getGid())); + + // Return the mapped group: + return group; + } + + protected abstract void executeQuery(); +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerImpl.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerImpl.java new file mode 100644 index 0000000..2f05e69 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalBrokerImpl.java @@ -0,0 +1,8 @@ +package org.ovirt.engine.core.bll.adbroker; + +public class LocalBrokerImpl extends LdapBrokerBase { + @Override + protected String getBrokerType() { + return "Local"; + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdGroupByGroupIdCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdGroupByGroupIdCommand.java new file mode 100644 index 0000000..b1e1157 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdGroupByGroupIdCommand.java @@ -0,0 +1,39 @@ +package org.ovirt.engine.core.bll.adbroker; + +import org.ovirt.engine.core.common.businessentities.LdapGroup; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class LocalGetAdGroupByGroupIdCommand extends LocalBrokerCommandBase { + // The log: + private static final Log log = LogFactory.getLog(LocalGetAdUserByUserIdCommand.class); + + public LocalGetAdGroupByGroupIdCommand(final LdapSearchByIdParameters parameters) { + super(parameters); + } + + private Guid getGroupId() { + return ((LdapSearchByIdParameters) getParameters()).getId(); + } + + @Override + protected void executeQuery() { + // Extract the local group id that is embedded in the GUID of the group: + final Guid guid = getGroupId(); + final int gid = guid2gid(guid); + + // Try to find the matching group: + final LocalGroup local = getDatabase().getGroupByGid(gid); + if (local != null) { + final LdapGroup group = mapGroup(local); + setReturnValue(group); + setSucceeded(true); + return; + } + + // No luck: + log.warn("Can't find group for id \"" + guid + "\"."); + setSucceeded(false); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdCommand.java new file mode 100644 index 0000000..fcc9775 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdCommand.java @@ -0,0 +1,39 @@ +package org.ovirt.engine.core.bll.adbroker; + +import org.ovirt.engine.core.common.businessentities.LdapUser; +import org.ovirt.engine.core.compat.Guid; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class LocalGetAdUserByUserIdCommand extends LocalBrokerCommandBase { + // The log: + private static final Log log = LogFactory.getLog(LocalGetAdUserByUserIdCommand.class); + + public LocalGetAdUserByUserIdCommand(final LdapSearchByIdParameters parameters) { + super(parameters); + } + + private Guid getUserId() { + return ((LdapSearchByIdParameters) getParameters()).getId(); + } + + @Override + protected void executeQuery() { + // Extract the local user id that is embedded in the GUID of the user: + final Guid guid = getUserId(); + final int uid = guid2uid(guid); + + // Try to find the matching user: + final LocalUser local = getDatabase().getUserByUid(uid); + if (local != null) { + final LdapUser user = mapUser(local); + setReturnValue(user); + setSucceeded(true); + return; + } + + // No luck: + log.warn("Can't find user for id \"" + guid + "\"."); + setSucceeded(false); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdListCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdListCommand.java new file mode 100644 index 0000000..b8bf692 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserIdListCommand.java @@ -0,0 +1,41 @@ +package org.ovirt.engine.core.bll.adbroker; + +import java.util.ArrayList; +import java.util.List; + +import org.ovirt.engine.core.common.businessentities.LdapUser; +import org.ovirt.engine.core.compat.Guid; + +public class LocalGetAdUserByUserIdListCommand extends LocalBrokerCommandBase { + public LocalGetAdUserByUserIdListCommand(final LdapSearchByIdListParameters parameters) { + super(parameters); + } + + private List<Guid> getUserIds() { + return ((LdapSearchByIdListParameters) getParameters()).getUserIds(); + } + + @Override + protected void executeQuery() { + // Extract the user identifiers from the given GUIDs: + final List<Guid> guids = getUserIds(); + final List<Integer> uids = new ArrayList<Integer>(guids.size()); + for (final Guid guid : guids) { + final int uid = guid2uid(guid); + uids.add(uid); + } + + // Get the list of users from the local database and build the + // corresponding group entities: + final List<LocalUser> locals = getDatabase().getUsersByUid(uids); + final List<LdapUser> groups = new ArrayList<LdapUser>(locals.size()); + for (final LocalUser local : locals) { + final LdapUser group = mapUser(local); + groups.add(group); + } + + // Return the results: + setReturnValue(groups); + setSucceeded(true); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserNameCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserNameCommand.java new file mode 100644 index 0000000..c434b1c --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGetAdUserByUserNameCommand.java @@ -0,0 +1,35 @@ +package org.ovirt.engine.core.bll.adbroker; + +import org.ovirt.engine.core.common.businessentities.LdapUser; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class LocalGetAdUserByUserNameCommand extends LocalBrokerCommandBase { + // The log: + private static final Log log = LogFactory.getLog(LocalGetAdUserByUserNameCommand.class); + + public LocalGetAdUserByUserNameCommand(final LdapSearchByUserNameParameters parameters) { + super(parameters); + } + + private String getUserName() { + return ((LdapSearchByUserNameParameters) getParameters()).getUserName(); + } + + @Override + protected void executeQuery() { + // Try to find the matching user: + final String name = getUserName(); + final LocalUser local = getDatabase().getUserByName(name); + if (local != null) { + final LdapUser user = mapUser(local); + setReturnValue(user); + setSucceeded(true); + return; + } + + // No luck: + log.warn("Can't find user for name \"" + name + "\"."); + setSucceeded(false); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGroup.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGroup.java new file mode 100644 index 0000000..6d097af --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalGroup.java @@ -0,0 +1,28 @@ +package org.ovirt.engine.core.bll.adbroker; + +/** + * A simple representation of a local group. Note that this is not complete + * because we don't need all the properties of groups. + */ +public class LocalGroup { + // The attributes of the group: + private long gid; + private String name; + + public LocalGroup(long gid, String name) { + this.gid = gid; + this.name = name; + } + + public long getGid() { + return gid; + } + + public String getName() { + return name; + } + + public String toString() { + return name; + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchGroupsByQueryCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchGroupsByQueryCommand.java new file mode 100644 index 0000000..6fd9db2 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchGroupsByQueryCommand.java @@ -0,0 +1,48 @@ +package org.ovirt.engine.core.bll.adbroker; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.ovirt.engine.core.common.businessentities.LdapGroup; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class LocalSearchGroupsByQueryCommand extends LocalBrokerCommandBase { + // The log: + private static final Log log = LogFactory.getLog(LocalSearchGroupsByQueryCommand.class); + + public LocalSearchGroupsByQueryCommand(final LdapSearchByQueryParameters parameters) { + super(parameters); + } + + @Override + protected void executeQuery() { + // Find the search criteria that the user typed in the search box: + final LdapSearchByQueryParameters queryParameters = (LdapSearchByQueryParameters) getParameters(); + final LdapQueryData queryData = queryParameters.getLdapQueryData(); + final String queryText = queryData.getQueryText(); + final String filterText = "^" + queryText.replace("*", ".*") + "$"; + final Pattern filterPattern = Pattern.compile(filterText); + log.info("Filter pattern is \"" + filterPattern + "\"."); + + // This will be the result: + final List<LdapGroup> results = new ArrayList<LdapGroup>(); + + // Iterate the groups in the database and select those whose that are + // not primary and whose name matches the pattern: + final List<LocalGroup> locals = getDatabase().getNonPrimaryGroups(); + for (final LocalGroup local : locals) { + final Matcher nameMatcher = filterPattern.matcher(local.getName()); + if (nameMatcher.find()) { + final LdapGroup group = mapGroup(local); + results.add(group); + } + } + + // Return the resulting list of groups: + setReturnValue(results); + setSucceeded(true); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchUserByQueryCommand.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchUserByQueryCommand.java new file mode 100644 index 0000000..5b8e81b --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalSearchUserByQueryCommand.java @@ -0,0 +1,47 @@ +package org.ovirt.engine.core.bll.adbroker; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.ovirt.engine.core.common.businessentities.LdapUser; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class LocalSearchUserByQueryCommand extends LocalBrokerCommandBase { + // The log: + private static final Log log = LogFactory.getLog(LocalSearchUserByQueryCommand.class); + + public LocalSearchUserByQueryCommand(final LdapSearchByQueryParameters parameters) { + super(parameters); + } + + @Override + protected void executeQuery() { + // Find the search criteria that the user typed in the search box: + final LdapSearchByQueryParameters queryParameters = (LdapSearchByQueryParameters) getParameters(); + final LdapQueryData queryData = queryParameters.getLdapQueryData(); + final String queryText = queryData.getQueryText(); + final String filterText = "^" + queryText.replace("*", ".*") + "$"; + final Pattern filterPattern = Pattern.compile(filterText); + log.info("Filter pattern is \"" + filterPattern + "\"."); + + // These will be the results: + final List<LdapUser> results = new ArrayList<LdapUser>(); + + // Run the query: + for (final LocalUser local : getDatabase().getAllUsers()) { + final Matcher nameMatcher = filterPattern.matcher(local.getName()); + final Matcher gecosMatcher = filterPattern.matcher(local.getGecos()); + if (nameMatcher.find() || gecosMatcher.find()) { + final LdapUser user = mapUser(local); + results.add(user); + } + } + + // Return the resulting list of users: + setReturnValue(results); + setSucceeded(true); + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUser.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUser.java new file mode 100644 index 0000000..c4516ea --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUser.java @@ -0,0 +1,41 @@ +package org.ovirt.engine.core.bll.adbroker; + +/** + * A simple representation of a local user. Note that this is not complete + * because we don't need all the properties of users, for example we don't need + * the home directory, so it is not included. + */ +public class LocalUser { + // The attributes of the user: + private int uid; + private int gid; + private String name; + private String gecos; + + public LocalUser(int uid, int gid, String name, String gecos) { + this.uid = uid; + this.gid = gid; + this.name = name; + this.gecos = gecos; + } + + public int getUid() { + return uid; + } + + public int getGid() { + return gid; + } + + public String getName() { + return name; + } + + public String getGecos() { + return gecos; + } + + public String toString() { + return name; + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserAuthenticator.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserAuthenticator.java new file mode 100644 index 0000000..6b92d10 --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserAuthenticator.java @@ -0,0 +1,127 @@ +package org.ovirt.engine.core.bll.adbroker; + +import java.util.concurrent.TimeUnit; + +import org.apache.sshd.ClientSession; +import org.apache.sshd.SshClient; +import org.apache.sshd.client.future.AuthFuture; +import org.apache.sshd.client.future.ConnectFuture; +import org.apache.sshd.common.future.CloseFuture; +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +public class LocalUserAuthenticator { + // The log: + private static final Log log = LogFactory.getLog(LocalUserAuthenticator.class); + + // The host and port to connect to with SSH for user authentication (don't + // see any reason to have this configurable at the moment): + private static final String SSH_HOST = "localhost"; + private static final int SSH_PORT = 22; + private static final int SSH_TIMEOUT = 10; + + // Timeout for the authentication operation (this should probably be + // configurable): + private static final int SSH_AUTH_TIMEOUT = 10; + + // The SSH client used to check authentication: + private SshClient sshClient; + + protected synchronized SshClient getSshClient() { + if (sshClient == null) { + createSshClient(); + } + return sshClient; + } + + private void createSshClient() { + // Create the SSH client: + sshClient = SshClient.setUpDefaultClient(); + sshClient.start(); + log.info("Local auth SSH client created and started."); + + // Remember to destroy it during shutdown: + Runtime.getRuntime().addShutdownHook( + new Thread("Destroy local authentication SSH client") { + public void run() { + sshClient.stop(); + log.info("Local auth SSH client stopped."); + } + } + ); + } + + protected ClientSession openSshSession() { + try { + log.info( + "Requesting SSH connection to host \"" + SSH_HOST + + "\" and port \"" + SSH_PORT + "\"."); + final ConnectFuture future = getSshClient().connect(SSH_HOST, SSH_PORT); + future.await(SSH_TIMEOUT, TimeUnit.SECONDS); + if (!future.isConnected()) { + log.error( + "Failed to create SSH connection to host \"" + SSH_HOST + + "\" and port \"" + SSH_PORT + "\", will return null."); + return null; + } + else { + log.info( + "Created SSH connection to host \"" + SSH_HOST + + "\" and port \"" + SSH_PORT + "\"."); + return future.getSession(); + } + } + catch (Exception exception) { + log.error( + "Failed to create SSH connection to host \"" + SSH_HOST + + "\" and port \"" + SSH_PORT + "\", will return null.", + exception); + return null; + } + } + + protected void closeSshSession(final ClientSession sshSession) { + try { + log.info("Requesting close of SSH connection."); + final CloseFuture future = sshSession.close(true); + future.await(SSH_TIMEOUT, TimeUnit.SECONDS); + if (!future.isClosed()) { + log.warn("Failed to close SSH connection, will continue anyway."); + } + else { + log.info("Closed SSH connection."); + } + } + catch (Exception exception) { + log.warn("Failed to close SSH connection, will continue anyway.", exception); + } + } + + public boolean authenticate(final String login, final String password) { + // Open a SSH connection to do the authentication (and remember to close it once done): + final ClientSession sshSession = openSshSession(); + try { + final AuthFuture future = sshSession.authPassword(login, password); + future.await(SSH_AUTH_TIMEOUT, TimeUnit.SECONDS); + if (future.isSuccess()) { + log.info( + "SSH authentication for user \"" + login + + "\" finished and the user is authenticated."); + return true; + } + else { + log.info( + "SSH authentication for user \"" + login + + "\" finished but the user is not authenticated."); + return false; + } + } + catch (Exception exception) { + log.error("Failed to perform SSH authentication.", exception); + return false; + } + finally { + closeSshSession(sshSession); + } + } +} diff --git a/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserDatabase.java b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserDatabase.java new file mode 100644 index 0000000..c67fe1c --- /dev/null +++ b/backend/manager/modules/bll/src/main/java/org/ovirt/engine/core/bll/adbroker/LocalUserDatabase.java @@ -0,0 +1,285 @@ +package org.ovirt.engine.core.bll.adbroker; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Properties; + +import org.ovirt.engine.core.utils.log.Log; +import org.ovirt.engine.core.utils.log.LogFactory; + +import com.sun.jna.Library; +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; + +public class LocalUserDatabase { + // The log: + private static final Log log = LogFactory.getLog(LocalUserDatabase.class); + + public static class passwd extends Structure { + public String pw_name; + public String pw_passwd; + public int pw_uid; + public int pw_gid; + public String pw_gecos; + public String pw_dir; + public String pw_shell; + }; + + public static class group extends Structure { + public String gr_name; + public String gr_passwd; + public int gr_gid; + public Pointer gr_mem; + }; + + // A subset of the methods of the C library that we need to handle the + // users and groups databases: + private interface C extends Library { + // Functions to iterate over the database of users: + void setpwent(); + void endpwent(); + passwd getpwent(); + + // Functions to iterate over the database of groups: + void setgrent(); + void endgrent(); + group getgrent(); + + // Functions to retrieve a specific entry from the database of users: + int getpwnam_r(String name, passwd pwd, ByteBuffer buf, long buflen, PointerByReference result); + int getpwuid_r(int uid, passwd pwd, ByteBuffer buf, long buflen, PointerByReference result); + + // Functions to retrieve a specific entry from the database of groups: + int getgrnam_r(String name, group grp, ByteBuffer buf, long buflen, PointerByReference result); + int getgrgid_r(int gid, group grp, ByteBuffer buf, long buflen, PointerByReference result); + + // Function to retrieve the list of members of a group: + int getgrouplist(String user, int group, int[] groups, IntByReference ngroups); + + // We need this to decide what should be the size of the buffer passed + // to the whatever_r functions above: + int _SC_GETGR_R_SIZE_MAX = 69; + int _SC_GETPW_R_SIZE_MAX = 70; + long sysconf(int name); + }; + + // Load the native C library: + private C c = (C) Native.loadLibrary("c", C.class); + + // The locations of relevant files: + private static final File LOGIN_DEFS_FILE = new File("/etc/login.defs"); + + // Parameters loaded from /etc/login.defs file: + private long uidMin = 1000; + private long uidMax = 60000; + private long gidMin = 1000; + private long gidMax = 60000; + + // The sizes of the buffers used by the functions that retrieve users and + // groups: + private int pwdBufferSize; + private int grpBufferSize; + + public LocalUserDatabase() { + // This is loaded only once, when the instance is created, so if it + // needs to be reloaded the instance will need to be created, which + // probably means that the engine will need to be restarted: + loadLoginDefs(); + + // Get the sizes of the buffers: + pwdBufferSize = (int) c.sysconf(C._SC_GETPW_R_SIZE_MAX); + grpBufferSize = (int) c.sysconf(C._SC_GETGR_R_SIZE_MAX); + } + + private void loadLoginDefs() { + // Do nothing if the file doesn't exist: + if (!LOGIN_DEFS_FILE.exists()) { + log.warn("The file \"" + LOGIN_DEFS_FILE.getAbsolutePath() + "\" doesn't exist."); + return; + } + + // Try to load it: + FileReader loginDefsReader = null; + Properties loginDefs = null; + try { + loginDefsReader = new FileReader(LOGIN_DEFS_FILE); + loginDefs = new Properties(); + loginDefs.load(loginDefsReader); + } + catch (IOException exception) { + log.error("Error while loading file \"" + LOGIN_DEFS_FILE.getAbsolutePath() + "\"."); + return; + } + finally { + try { + loginDefsReader.close(); + } + catch (IOException exception) { + log.warn("Error while closing file \"" + LOGIN_DEFS_FILE.getAbsolutePath() + "\" ignored."); + } + } + + // Load the upper and lower limits of users and groups identifiers: + uidMin = Long.valueOf(loginDefs.getProperty("UID_MIN")); + uidMax = Long.valueOf(loginDefs.getProperty("UID_MAX")); + gidMin = Long.valueOf(loginDefs.getProperty("GID_MIN")); + gidMax = Long.valueOf(loginDefs.getProperty("GID_MAX")); + } + + private LocalUser mapUser(passwd pwd) { + if (pwd.pw_uid != 0 && (pwd.pw_uid < uidMin || pwd.pw_uid > uidMax)) { + return null; + } + return new LocalUser(pwd.pw_uid, pwd.pw_gid, pwd.pw_name, pwd.pw_gecos); + } + + private LocalGroup mapGroup(group grp) { + if (grp.gr_gid < gidMin || grp.gr_gid > gidMax) { + return null; + } + return new LocalGroup(grp.gr_gid, grp.gr_name); + } + + /** + * Returns the complete list of users stored in the local database. + */ + public synchronized List<LocalUser> getAllUsers() { + final List<LocalUser> result = new ArrayList<LocalUser>(); + try { + c.setpwent(); + passwd pwd = null; + while ((pwd = c.getpwent()) != null) { + final LocalUser user = mapUser(pwd); + if (user != null) { + result.add(user); + } + } + } + finally { + c.endpwent(); + } + return result; + } + + /** + * Returns the complete list of groups stored in the local database. This + * includes primary groups, so it is usually better to use + * <code>getNonPrimaryGroups</code> instead. + */ + public synchronized List<LocalGroup> getAllGroups() { + final List<LocalGroup> result = new ArrayList<LocalGroup>(); + try { + c.setgrent(); + group grp = null; + while ((grp = c.getgrent()) != null) { + final LocalGroup group = mapGroup(grp); + if (group != null) { + result.add(group); + } + } + } + finally { + c.endgrent(); + } + return result; + } + + public synchronized List<LocalGroup> getNonPrimaryGroups() { + final List<LocalGroup> results = new ArrayList<LocalGroup>(); + for (LocalGroup group : getAllGroups()) { + final LocalUser user = getUserByName(group.getName()); + if (user == null || user.getGid() != group.getGid()) { + results.add(group); + } + } + return results; + } + + public LocalUser getUserByName(String name) { + final passwd pwd = new passwd(); + final PointerByReference result = new PointerByReference(); + final ByteBuffer buffer = ByteBuffer.allocateDirect(pwdBufferSize); + c.getpwnam_r(name, pwd, buffer, (long) buffer.capacity(), result); + if (result.getPointer() == null) { + return null; + } + return mapUser(pwd); + } + + public LocalUser getUserByUid(final int uid) { + final passwd pwd = new passwd(); + final PointerByReference result = new PointerByReference(); + final ByteBuffer buffer = ByteBuffer.allocateDirect(pwdBufferSize); + c.getpwuid_r(uid, pwd, buffer, (long) buffer.capacity(), result); + if (result.getPointer() == null) { + return null; + } + return mapUser(pwd); + } + + public List<LocalUser> getUsersByUid(final List<Integer> uids) { + final List<LocalUser> result = new ArrayList<LocalUser>(); + for (int uid : uids) { + final LocalUser user = getUserByUid(uid); + if (user != null) { + result.add(user); + } + } + return result; + } + + public LocalGroup getGroupByName(final String name) { + final group grp = new group(); + final PointerByReference result = new PointerByReference(); + final ByteBuffer buffer = ByteBuffer.allocateDirect(grpBufferSize); + c.getgrnam_r(name, grp, buffer, (long) buffer.capacity(), result); + if (result.getPointer() == null) { + return null; + } + return mapGroup(grp); + } + + public List<LocalGroup> getGroupsByUserName(final String name) { + final LocalUser user = getUserByName(name); + if (user == null) { + return Collections.emptyList(); + } + final List<LocalGroup> results = new ArrayList<LocalGroup>(); + final IntByReference size = new IntByReference(4); + int[] gids = new int[size.getValue()]; + for (;;) { + if (c.getgrouplist(name, user.getGid(), gids, size) != -1) { + break; + } + gids = new int[size.getValue()]; + } + for (int gid : gids) { + if (gid != user.getGid()) { + LocalGroup group = getGroupByGid(gid); + if (group != null) { + results.add(group); + } + } + } + return results; + } + + public LocalGroup getGroupByGid(final int gid) { + final group grp = new group(); + final PointerByReference result = new PointerByReference(); + final ByteBuffer buffer = ByteBuffer.allocateDirect(grpBufferSize); + c.getgrgid_r(gid, grp, buffer, (long) buffer.capacity(), result); + if (result.getPointer() == null) { + return null; + } + return mapGroup(grp); + } +} diff --git a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java index 64a191d..981f6ea 100644 --- a/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java +++ b/backend/manager/modules/common/src/main/java/org/ovirt/engine/core/common/config/ConfigValues.java @@ -711,6 +711,14 @@ @DefaultValueAttribute("") PredefinedVMProperties(273), + /** + * The name of the domain used for local authentication. This is + * relevant only for display, as it is not a real domain name. + */ + @TypeConverterAttribute(String.class) + @DefaultValueAttribute("local") + LocalDomain(316), + @TypeConverterAttribute(Integer.class) @DefaultValueAttribute("250") MaxNumberOfHostsInStoragePool(274), diff --git a/packaging/fedora/spec/ovirt-engine.spec.in b/packaging/fedora/spec/ovirt-engine.spec.in index 4b8b98c..231ac62 100644 --- a/packaging/fedora/spec/ovirt-engine.spec.in +++ b/packaging/fedora/spec/ovirt-engine.spec.in @@ -220,6 +220,7 @@ Requires: java Requires: javassist Requires: jboss-interceptors-1.1-api +Requires: jna Requires: objectweb-asm Requires: openssh Requires: openssl @@ -419,7 +420,8 @@ # spring-asm *IS* required on rhel-6 rm -rf %{buildroot}%{engine_ear}/lib/spring-asm*.jar -# Then for the system jar files (using build-classpath): +# Replace jar files in the .ear lib directory with links to their actual +# locations: while read jar_name lib_path do rm -rf %{buildroot}%{engine_ear}/lib/${lib_path}*.jar @@ -436,6 +438,7 @@ dom4j dom4j geronimo-validation validation-api hibernate-validator hibernate-validator +jna jna objectweb-asm/asm-all asm-all quartz quartz slf4j/api slf4j-api diff --git a/pom.xml b/pom.xml index 815d3f7..b1c861d 100644 --- a/pom.xml +++ b/pom.xml @@ -88,6 +88,7 @@ <maven-compiler-plugin.version>2.3.2</maven-compiler-plugin.version> <gwt.plugin.version>2.3.0</gwt.plugin.version> <test-jar.plugin.version>2.2</test-jar.plugin.version> + <jna.version>3.4.0</jna.version> </properties> <dependencyManagement> <dependencies> @@ -260,6 +261,11 @@ <version>${gwt.version}</version> <scope>runtime</scope> </dependency> + <dependency> + <groupId>net.java.dev.jna</groupId> + <artifactId>jna</artifactId> + <version>${jna.version}</version> + </dependency> </dependencies> </dependencyManagement> <dependencies> -- To view, visit http://gerrit.ovirt.org/11863 To unsubscribe, visit http://gerrit.ovirt.org/settings Gerrit-MessageType: newchange Gerrit-Change-Id: Ia418368dc8959e3e176a7f252fcc91fb682045ff Gerrit-PatchSet: 1 Gerrit-Project: ovirt-engine Gerrit-Branch: master Gerrit-Owner: Juan Hernandez <juan.hernan...@redhat.com> _______________________________________________ Engine-patches mailing list Engine-patches@ovirt.org http://lists.ovirt.org/mailman/listinfo/engine-patches