/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.sling.auth.xing.login.impl; import java.util.Dictionary; import javax.jcr.Credentials; import javax.jcr.RepositoryException; import javax.jcr.Session; import javax.jcr.Value; import javax.jcr.ValueFactory; import org.apache.commons.lang.StringUtils; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Modified; import org.apache.felix.scr.annotations.Properties; import org.apache.felix.scr.annotations.Property; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.Service; import org.apache.jackrabbit.api.security.user.User; import org.apache.jackrabbit.api.security.user.UserManager; import org.apache.sling.auth.xing.api.AbstractXingUserManager; import org.apache.sling.auth.xing.api.XingUser; import org.apache.sling.auth.xing.login.XingLogin; import org.apache.sling.auth.xing.login.XingLoginUserManager; import org.apache.sling.auth.xing.login.XingLoginUtil; import org.apache.sling.commons.osgi.PropertiesUtil; import org.apache.sling.jcr.api.SlingRepository; import org.osgi.framework.Constants; import org.osgi.service.component.ComponentContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component( label = "Apache Sling Authentication XING Login “Default User Manager”", description = "Default User Manager for Sling Authentication XING Login", immediate = true, metatype = true ) @Service @Properties({ @Property(name = Constants.SERVICE_VENDOR, value = XingLogin.SERVICE_VENDOR), @Property(name = Constants.SERVICE_DESCRIPTION, value = "Default User Manager for Sling Authentication XING Login"), @Property(name = Constants.SERVICE_RANKING, intValue = 0, propertyPrivate = false) }) public class DefaultXingLoginUserManager extends AbstractXingUserManager implements XingLoginUserManager { private String secretKey; private String userDataProperty; private String userHashProperty; @Reference private SlingRepository slingRepository; private static final String DEFAULT_USER_DATA_PROPERTY = "data"; private static final String DEFAULT_USER_HASH_PROPERTY = "hash"; @Property(value = "") private static final String SECRET_KEY_PARAMETER = "org.apache.sling.auth.xing.login.impl.DefaultXingLoginUserManager.secretKey"; @Property(value = DEFAULT_USER_DATA_PROPERTY) private static final String USER_DATA_PROPERTY_PARAMETER = "org.apache.sling.auth.xing.login.impl.DefaultXingLoginUserManager.user.property.data"; @Property(value = DEFAULT_USER_HASH_PROPERTY) private static final String USER_HASH_PROPERTY_PARAMETER = "org.apache.sling.auth.xing.login.impl.DefaultXingLoginUserManager.user.property.hash"; @Property(boolValue = DEFAULT_AUTO_CREATE_USER) private static final String AUTO_CREATE_USER_PARAMETER = "org.apache.sling.auth.xing.login.impl.DefaultXingLoginUserManager.user.create.auto"; @Property(boolValue = DEFAULT_AUTO_UPDATE_USER) private static final String AUTO_UPDATE_USER_PARAMETER = "org.apache.sling.auth.xing.login.impl.DefaultXingLoginUserManager.user.update.auto"; private final Logger logger = LoggerFactory.getLogger(DefaultXingLoginUserManager.class); public DefaultXingLoginUserManager() { } @Activate protected void activate(final ComponentContext componentContext) { logger.debug("activate"); configure(componentContext); } @Modified protected void modified(final ComponentContext componentContext) { logger.debug("modified"); configure(componentContext); } @Deactivate protected void deactivate(final ComponentContext componentContext) { logger.debug("deactivate"); if (session != null) { session.logout(); session = null; } } protected synchronized void configure(final ComponentContext componentContext) { final Dictionary properties = componentContext.getProperties(); secretKey = PropertiesUtil.toString(properties.get(SECRET_KEY_PARAMETER), "").trim(); userDataProperty = PropertiesUtil.toString(properties.get(USER_DATA_PROPERTY_PARAMETER), DEFAULT_USER_DATA_PROPERTY).trim(); userHashProperty = PropertiesUtil.toString(properties.get(USER_HASH_PROPERTY_PARAMETER), DEFAULT_USER_HASH_PROPERTY).trim(); autoCreateUser = PropertiesUtil.toBoolean(properties.get(AUTO_CREATE_USER_PARAMETER), DEFAULT_AUTO_CREATE_USER); autoUpdateUser = PropertiesUtil.toBoolean(properties.get(AUTO_UPDATE_USER_PARAMETER), DEFAULT_AUTO_UPDATE_USER); if (StringUtils.isEmpty(secretKey)) { logger.warn("configured secret key is empty"); } } @Override protected SlingRepository getSlingRepository() { return slingRepository; } @Override public User createUser(final Credentials credentials) { logger.debug("create user"); return storeUser(credentials); } @Override public User updateUser(Credentials credentials) { logger.debug("update user"); return storeUser(credentials); } protected User storeUser(Credentials credentials) { final String givenHash = XingLoginUtil.getHash(credentials); final String json = XingLoginUtil.getUser(credentials); if (givenHash == null || json == null) { logger.debug("unable to get hash and/or user data from given credentials"); return null; } // validate user data with hash try { final String computedHash = XingLoginUtil.hash(json, secretKey, XingLogin.HASH_ALGORITHM); final boolean match = givenHash.equals(computedHash); if (!match) { logger.warn("invalid hash or user data given, aborting"); return null; } } catch (Exception e) { logger.error(e.getMessage(), e); return null; } try { final XingUser xingUser = XingLoginUtil.fromJson(json); final String userId = xingUser.getId(); // TODO make configurable User user = getUser(userId); if (user == null) { logger.debug("creating a new user with id '{}'", userId); final Session session = getSession(); final UserManager userManager = getUserManager(session); user = userManager.createUser(userId, null); } else { logger.debug("updating an existing user with id '{}'", userId); } // TODO disable user on create? final ValueFactory valueFactory = getSession().getValueFactory(); final Value dataValue = valueFactory.createValue(json); final Value hashValue = valueFactory.createValue(givenHash); user.setProperty(userDataProperty, dataValue); user.setProperty(userHashProperty, hashValue); session.save(); return user; } catch (Exception e) { logger.error(e.getMessage(), e); return null; } } @Override public String getHash(final User user) { try { final Value[] values = user.getProperty(userHashProperty); if (values != null && values.length == 1) { final Value value = values[0]; return value.getString(); } } catch (RepositoryException e) { logger.error(e.getMessage(), e); } return null; } }