/* * oxAuth is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. * * Copyright (c) 2014, Gluu */ package org.xdi.oxauth.service.fido.u2f; import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.ejb.Stateless; import javax.inject.Inject; import javax.inject.Named; import org.gluu.site.ldap.persistence.BatchOperation; import org.gluu.site.ldap.persistence.LdapEntryManager; import org.slf4j.Logger; import org.xdi.ldap.model.SearchScope; import org.xdi.ldap.model.SimpleBranch; import org.xdi.oxauth.model.config.StaticConfiguration; import org.xdi.oxauth.model.fido.u2f.DeviceRegistration; import org.xdi.oxauth.model.fido.u2f.DeviceRegistrationStatus; import org.xdi.oxauth.model.util.Base64Util; import org.xdi.oxauth.service.CleanerTimer; import org.xdi.oxauth.service.UserService; import org.xdi.util.StringHelper; import com.unboundid.ldap.sdk.Filter; /** * Provides operations with user U2F devices * * @author Yuriy Movchan Date: 05/14/2015 */ @Stateless @Named public class DeviceRegistrationService { @Inject private Logger log; @Inject private LdapEntryManager ldapEntryManager; @Inject private UserService userService; @Inject private StaticConfiguration staticConfiguration; public void addBranch(final String userInum) { SimpleBranch branch = new SimpleBranch(); branch.setOrganizationalUnitName("fido"); branch.setDn(getBaseDnForU2fUserDevices(userInum)); ldapEntryManager.persist(branch); } public boolean containsBranch(final String userInum) { return ldapEntryManager.contains(SimpleBranch.class, getBaseDnForU2fUserDevices(userInum)); } public void prepareBranch(final String userInum) { // Create U2F user device registrations branch if needed if (!containsBranch(userInum)) { addBranch(userInum); } } public DeviceRegistration findUserDeviceRegistration(String userInum, String deviceId, String... returnAttributes) { prepareBranch(userInum); String deviceDn = getDnForU2fDevice(userInum, deviceId); return ldapEntryManager.find(DeviceRegistration.class, deviceDn, returnAttributes); } public List<DeviceRegistration> findUserDeviceRegistrations(String userInum, String appId, String ... returnAttributes) { prepareBranch(userInum); String baseDnForU2fDevices = getBaseDnForU2fUserDevices(userInum); Filter appIdFilter = Filter.createEqualityFilter("oxApplication", appId); return ldapEntryManager.findEntries(baseDnForU2fDevices, DeviceRegistration.class, returnAttributes, appIdFilter); } public List<DeviceRegistration> findDeviceRegistrationsByKeyHandle(String appId, String keyHandle, String ... returnAttributes) { if (org.xdi.util.StringHelper.isEmpty(appId) || StringHelper.isEmpty(keyHandle)) { return new ArrayList<DeviceRegistration>(0); } byte[] keyHandleDecoded = Base64Util.base64urldecode(keyHandle); String baseDn = userService.getDnForUser(null); Filter deviceObjectClassFilter = Filter.createEqualityFilter("objectClass", "oxDeviceRegistration"); Filter deviceHashCodeFilter = Filter.createEqualityFilter("oxDeviceHashCode", String.valueOf(getKeyHandleHashCode(keyHandleDecoded))); Filter deviceKeyHandleFilter = Filter.createEqualityFilter("oxDeviceKeyHandle", keyHandle); Filter appIdFilter = Filter.createEqualityFilter("oxApplication", appId); Filter filter = Filter.createANDFilter(deviceObjectClassFilter, deviceHashCodeFilter, appIdFilter, deviceKeyHandleFilter); return ldapEntryManager.findEntries(baseDn, DeviceRegistration.class, returnAttributes, filter); } public DeviceRegistration findOneStepUserDeviceRegistration(String deviceId, String... returnAttributes) { String deviceDn = getDnForOneStepU2fDevice(deviceId); return ldapEntryManager.find(DeviceRegistration.class, deviceDn); } public void addUserDeviceRegistration(String userInum, DeviceRegistration deviceRegistration) { prepareBranch(userInum); ldapEntryManager.persist(deviceRegistration); } public boolean attachUserDeviceRegistration(String userInum, String oneStepDeviceId) { String oneStepDeviceDn = getDnForOneStepU2fDevice(oneStepDeviceId); // Load temporary stored device registration DeviceRegistration deviceRegistration = ldapEntryManager.find(DeviceRegistration.class, oneStepDeviceDn); if (deviceRegistration == null) { return false; } // Remove temporary stored device registration removeUserDeviceRegistration(deviceRegistration); // Attach user device registration to user String deviceDn = getDnForU2fDevice(userInum, deviceRegistration.getId()); deviceRegistration.setDn(deviceDn); addUserDeviceRegistration(userInum, deviceRegistration); return true; } public void addOneStepDeviceRegistration(DeviceRegistration deviceRegistration) { ldapEntryManager.persist(deviceRegistration); } public void updateDeviceRegistration(String userInum, DeviceRegistration deviceRegistration) { prepareBranch(userInum); ldapEntryManager.merge(deviceRegistration); } public void disableUserDeviceRegistration(DeviceRegistration deviceRegistration) { deviceRegistration.setStatus(DeviceRegistrationStatus.COMPROMISED); ldapEntryManager.merge(deviceRegistration); } public void removeUserDeviceRegistration(DeviceRegistration deviceRegistration) { ldapEntryManager.remove(deviceRegistration); } public List<DeviceRegistration> getExpiredDeviceRegistrations(BatchOperation<DeviceRegistration> batchOperation, Date expirationDate) { final String u2fBaseDn = getDnForOneStepU2fDevice(null); Filter expirationFilter = Filter.createLessOrEqualFilter("creationDate", ldapEntryManager.encodeGeneralizedTime(expirationDate)); List<DeviceRegistration> deviceRegistrations = ldapEntryManager.findEntries(u2fBaseDn, DeviceRegistration.class, expirationFilter, SearchScope.SUB, null, batchOperation, 0, CleanerTimer.BATCH_SIZE, CleanerTimer.BATCH_SIZE); return deviceRegistrations; } /** * Build DN string for U2F user device */ public String getDnForU2fDevice(String userInum, String oxId) { String baseDnForU2fDevices = getBaseDnForU2fUserDevices(userInum); if (StringHelper.isEmpty(oxId)) { return baseDnForU2fDevices; } return String.format("oxId=%s,%s", oxId, baseDnForU2fDevices); } public String getBaseDnForU2fUserDevices(String userInum) { final String userBaseDn = userService.getDnForUser(userInum); // "ou=fido,inum=1234,ou=people,o=@!1111,o=gluu" return String.format("ou=fido,%s", userBaseDn); } public String getDnForOneStepU2fDevice(String deviceRegistrationId) { final String u2fBaseDn = staticConfiguration.getBaseDn().getU2fBase(); // ou=registered_devices,ou=u2f,o=@!1111,o=gluu if (StringHelper.isEmpty(deviceRegistrationId)) { return String.format("ou=registered_devices,%s", u2fBaseDn); } return String.format("oxid=%s,ou=registered_devices,%s", deviceRegistrationId, u2fBaseDn); } /* * Generate non unique hash code to split keyHandle among small cluster with 10-20 elements * * This hash code will be used to generate small LDAP indexes */ public int getKeyHandleHashCode(byte[] keyHandle) { int hash = 0; for (int j = 0; j < keyHandle.length; j++) { hash += keyHandle[j]*j; } return hash; } }