blob: 654307b43b8e7de7ef672d4740952ba22b8c8843 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. 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. For additional information regarding
* copyright in this work, please see the NOTICE file in the top level
* directory of this distribution.
*/
package org.apache.roller.weblogger.business.jpa;
import java.sql.Timestamp;
import javax.persistence.NoResultException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.roller.weblogger.WebloggerException;
import org.apache.roller.weblogger.business.UserManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import javax.persistence.TypedQuery;
import org.apache.roller.weblogger.config.WebloggerConfig;
import org.apache.roller.weblogger.pojos.GlobalPermission;
import org.apache.roller.weblogger.pojos.RollerPermission;
import org.apache.roller.weblogger.pojos.User;
import org.apache.roller.weblogger.pojos.UserRole;
import org.apache.roller.weblogger.pojos.Weblog;
import org.apache.roller.weblogger.pojos.WeblogPermission;
@com.google.inject.Singleton
public class JPAUserManagerImpl implements UserManager {
private static Log log = LogFactory.getLog(JPAUserManagerImpl.class);
private final JPAPersistenceStrategy strategy;
// cached mapping of userNames -> userIds
private Map<String, String> userNameToIdMap = Collections.synchronizedMap(new HashMap<String, String>());
@com.google.inject.Inject
protected JPAUserManagerImpl(JPAPersistenceStrategy strat) {
log.debug("Instantiating JPA User Manager");
this.strategy = strat;
}
@Override
public void release() {}
//--------------------------------------------------------------- user CRUD
@Override
public void saveUser(User data) throws WebloggerException {
this.strategy.store(data);
}
@Override
public void removeUser(User user) throws WebloggerException {
String userName = user.getUserName();
// remove permissions, maintaining both sides of relationship
List<WeblogPermission> perms = getWeblogPermissions(user);
for (WeblogPermission perm : perms) {
this.strategy.remove(perm);
}
this.strategy.remove(user);
// remove entry from cache mapping
this.userNameToIdMap.remove(userName);
}
@Override
public void addUser(User newUser) throws WebloggerException {
if (newUser == null) {
throw new WebloggerException("cannot add null user");
}
boolean adminUser = false;
List existingUsers = this.getUsers(Boolean.TRUE, null, null, 0, 1);
boolean firstUserAdmin = WebloggerConfig.getBooleanProperty("users.firstUserAdmin");
if (existingUsers.isEmpty() && firstUserAdmin) {
// Make first user an admin
adminUser = true;
//if user was disabled (because of activation user
// account with e-mail property), enable it for admin user
newUser.setEnabled(Boolean.TRUE);
newUser.setActivationCode(null);
}
if (getUserByUserName(newUser.getUserName()) != null ||
getUserByUserName(newUser.getUserName().toLowerCase()) != null) {
throw new WebloggerException("error.add.user.userNameInUse");
}
this.strategy.store(newUser);
grantRole("editor", newUser);
if (adminUser) {
grantRole("admin", newUser);
}
}
@Override
public User getUser(String id) throws WebloggerException {
return (User)this.strategy.load(User.class, id);
}
//------------------------------------------------------------ user queries
@Override
public User getUserByUserName(String userName) throws WebloggerException {
return getUserByUserName(userName, Boolean.TRUE);
}
@Override
public User getUserByOpenIdUrl(String openIdUrl) throws WebloggerException {
if (openIdUrl == null) {
throw new WebloggerException("OpenID URL cannot be null");
}
TypedQuery<User> query;
User user;
query = strategy.getNamedQuery(
"User.getByOpenIdUrl", User.class);
query.setParameter(1, openIdUrl);
try {
user = query.getSingleResult();
} catch (NoResultException e) {
user = null;
}
return user;
}
@Override
public User getUserByUserName(String userName, Boolean enabled)
throws WebloggerException {
if (userName==null) {
throw new WebloggerException("userName cannot be null");
}
// check cache first
// NOTE: if we ever allow changing usernames then this needs updating
if(this.userNameToIdMap.containsKey(userName)) {
User user = this.getUser(
this.userNameToIdMap.get(userName));
if (user != null) {
// only return the user if the enabled status matches
if(enabled == null || enabled.equals(user.getEnabled())) {
log.debug("userNameToIdMap CACHE HIT - "+userName);
return user;
}
} else {
// mapping hit with lookup miss? mapping must be old, remove it
this.userNameToIdMap.remove(userName);
}
}
// cache failed, do lookup
TypedQuery<User> query;
Object[] params;
if (enabled != null) {
query = strategy.getNamedQuery(
"User.getByUserName&Enabled", User.class);
params = new Object[] {userName, enabled};
} else {
query = strategy.getNamedQuery(
"User.getByUserName", User.class);
params = new Object[] {userName};
}
for (int i=0; i<params.length; i++) {
query.setParameter(i+1, params[i]);
}
User user;
try {
user = query.getSingleResult();
} catch (NoResultException e) {
user = null;
}
// add mapping to cache
if(user != null) {
log.debug("userNameToIdMap CACHE MISS - " + userName);
this.userNameToIdMap.put(user.getUserName(), user.getId());
}
return user;
}
@Override
public List<User> getUsers(Boolean enabled, Date startDate, Date endDate,
int offset, int length)
throws WebloggerException {
TypedQuery<User> query;
Timestamp end = new Timestamp(endDate != null ? endDate.getTime() : new Date().getTime());
if (enabled != null) {
if (startDate != null) {
Timestamp start = new Timestamp(startDate.getTime());
query = strategy.getNamedQuery(
"User.getByEnabled&EndDate&StartDateOrderByStartDateDesc", User.class);
query.setParameter(1, enabled);
query.setParameter(2, end);
query.setParameter(3, start);
} else {
query = strategy.getNamedQuery(
"User.getByEnabled&EndDateOrderByStartDateDesc", User.class);
query.setParameter(1, enabled);
query.setParameter(2, end);
}
} else {
if (startDate != null) {
Timestamp start = new Timestamp(startDate.getTime());
query = strategy.getNamedQuery(
"User.getByEndDate&StartDateOrderByStartDateDesc", User.class);
query.setParameter(1, end);
query.setParameter(2, start);
} else {
query = strategy.getNamedQuery(
"User.getByEndDateOrderByStartDateDesc", User.class);
query.setParameter(1, end);
}
}
if (offset != 0) {
query.setFirstResult(offset);
}
if (length != -1) {
query.setMaxResults(length);
}
return query.getResultList();
}
@Override
public List<User> getUsersStartingWith(String startsWith, Boolean enabled,
int offset, int length) throws WebloggerException {
TypedQuery<User> query;
if (enabled != null) {
if (startsWith != null) {
query = strategy.getNamedQuery(
"User.getByEnabled&UserNameOrEmailAddressStartsWith", User.class);
query.setParameter(1, enabled);
query.setParameter(2, startsWith + '%');
query.setParameter(3, startsWith + '%');
} else {
query = strategy.getNamedQuery(
"User.getByEnabled", User.class);
query.setParameter(1, enabled);
}
} else {
if (startsWith != null) {
query = strategy.getNamedQuery(
"User.getByUserNameOrEmailAddressStartsWith", User.class);
query.setParameter(1, startsWith + '%');
} else {
query = strategy.getNamedQuery("User.getAll", User.class);
}
}
if (offset != 0) {
query.setFirstResult(offset);
}
if (length != -1) {
query.setMaxResults(length);
}
return query.getResultList();
}
@Override
public Map<String, Long> getUserNameLetterMap() throws WebloggerException {
String lc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
Map<String, Long> results = new TreeMap<>();
TypedQuery<Long> query = strategy.getNamedQuery(
"User.getCountByUserNameLike", Long.class);
for (int i=0; i<26; i++) {
char currentChar = lc.charAt(i);
query.setParameter(1, currentChar + "%");
List row = query.getResultList();
Long count = (Long) row.get(0);
results.put(String.valueOf(currentChar), count);
}
return results;
}
@Override
public List<User> getUsersByLetter(char letter, int offset, int length)
throws WebloggerException {
TypedQuery<User> query = strategy.getNamedQuery(
"User.getByUserNameOrderByUserName", User.class);
query.setParameter(1, letter + "%");
if (offset != 0) {
query.setFirstResult(offset);
}
if (length != -1) {
query.setMaxResults(length);
}
return query.getResultList();
}
/**
* Get count of users, enabled only
*/
@Override
public long getUserCount() throws WebloggerException {
TypedQuery<Long> q = strategy.getNamedQuery("User.getCountEnabledDistinct", Long.class);
q.setParameter(1, Boolean.TRUE);
List<Long> results = q.getResultList();
return results.get(0);
}
@Override
public User getUserByActivationCode(String activationCode) throws WebloggerException {
if (activationCode == null) {
throw new WebloggerException("activationcode is null");
}
TypedQuery<User> q = strategy.getNamedQuery("User.getUserByActivationCode", User.class);
q.setParameter(1, activationCode);
try {
return q.getSingleResult();
} catch (NoResultException e) {
return null;
}
}
//-------------------------------------------------------- permissions CRUD
@Override
public boolean checkPermission(RollerPermission perm, User user) throws WebloggerException {
// if permission a weblog permission
if (perm instanceof WeblogPermission) {
// if user has specified permission in weblog return true
WeblogPermission permToCheck = (WeblogPermission)perm;
try {
RollerPermission existingPerm = getWeblogPermission(permToCheck.getWeblog(), user);
if (existingPerm != null && existingPerm.implies(perm)) {
return true;
}
} catch (WebloggerException ignored) {
}
}
// if Blog Server admin would still have weblog permission above
GlobalPermission globalPerm = new GlobalPermission(user);
if (globalPerm.implies(perm)) {
return true;
}
if (log.isDebugEnabled()) {
log.debug("PERM CHECK FAILED: user " + user.getUserName() + " does not have " + perm.toString());
}
return false;
}
@Override
public WeblogPermission getWeblogPermission(Weblog weblog, User user) throws WebloggerException {
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&WeblogId"
, WeblogPermission.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, weblog.getHandle());
try {
return q.getSingleResult();
} catch (NoResultException ignored) {
return null;
}
}
@Override
public WeblogPermission getWeblogPermissionIncludingPending(Weblog weblog, User user) throws WebloggerException {
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&WeblogIdIncludingPending",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, weblog.getHandle());
try {
return q.getSingleResult();
} catch (NoResultException ignored) {
return null;
}
}
@Override
public void grantWeblogPermission(Weblog weblog, User user, List<String> actions) throws WebloggerException {
// first, see if user already has a permission for the specified object
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&WeblogIdIncludingPending",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, weblog.getHandle());
WeblogPermission existingPerm = null;
try {
existingPerm = q.getSingleResult();
} catch (NoResultException ignored) {}
// permission already exists, so add any actions specified in perm argument
if (existingPerm != null) {
existingPerm.addActions(actions);
this.strategy.store(existingPerm);
} else {
// it's a new permission, so store it
WeblogPermission perm = new WeblogPermission(weblog, user, actions);
this.strategy.store(perm);
}
}
@Override
public void grantWeblogPermissionPending(Weblog weblog, User user, List<String> actions) throws WebloggerException {
// first, see if user already has a permission for the specified object
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&WeblogIdIncludingPending",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, weblog.getHandle());
WeblogPermission existingPerm = null;
try {
existingPerm = q.getSingleResult();
} catch (NoResultException ignored) {}
// permission already exists, so complain
if (existingPerm != null) {
throw new WebloggerException("Cannot make existing permission into pending permission");
} else {
// it's a new permission, so store it
WeblogPermission perm = new WeblogPermission(weblog, user, actions);
perm.setPending(true);
this.strategy.store(perm);
}
}
@Override
public void confirmWeblogPermission(Weblog weblog, User user) throws WebloggerException {
// get specified permission
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&WeblogIdIncludingPending",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, weblog.getHandle());
WeblogPermission existingPerm;
try {
existingPerm = q.getSingleResult();
} catch (NoResultException ignored) {
throw new WebloggerException("ERROR: permission not found");
}
// set pending to false
existingPerm.setPending(false);
this.strategy.store(existingPerm);
}
@Override
public void declineWeblogPermission(Weblog weblog, User user) throws WebloggerException {
// get specified permission
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&WeblogIdIncludingPending",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, weblog.getHandle());
WeblogPermission existingPerm;
try {
existingPerm = q.getSingleResult();
} catch (NoResultException ignored) {
throw new WebloggerException("ERROR: permission not found");
}
// remove permission
this.strategy.remove(existingPerm);
}
@Override
public void revokeWeblogPermission(Weblog weblog, User user, List<String> actions) throws WebloggerException {
// get specified permission
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&WeblogIdIncludingPending",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, weblog.getHandle());
WeblogPermission oldperm;
try {
oldperm = q.getSingleResult();
} catch (NoResultException ignored) {
throw new WebloggerException("ERROR: permission not found");
}
// remove actions specified in perm argument
oldperm.removeActions(actions);
if (oldperm.isEmpty()) {
// no actions left in permission so remove it
this.strategy.remove(oldperm);
} else {
// otherwise save it
this.strategy.store(oldperm);
}
}
@Override
public List<WeblogPermission> getWeblogPermissions(User user) throws WebloggerException {
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
return q.getResultList();
}
@Override
public List<WeblogPermission> getWeblogPermissions(Weblog weblog) throws WebloggerException {
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByWeblogId",
WeblogPermission.class);
q.setParameter(1, weblog.getHandle());
return q.getResultList();
}
@Override
public List<WeblogPermission> getWeblogPermissionsIncludingPending(Weblog weblog) throws WebloggerException {
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByWeblogIdIncludingPending",
WeblogPermission.class);
q.setParameter(1, weblog.getHandle());
return q.getResultList();
}
@Override
public List<WeblogPermission> getPendingWeblogPermissions(User user) throws WebloggerException {
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByUserName&Pending",
WeblogPermission.class);
q.setParameter(1, user.getUserName());
return q.getResultList();
}
@Override
public List<WeblogPermission> getPendingWeblogPermissions(Weblog weblog) throws WebloggerException {
TypedQuery<WeblogPermission> q = strategy.getNamedQuery("WeblogPermission.getByWeblogId&Pending",
WeblogPermission.class);
q.setParameter(1, weblog.getHandle());
return q.getResultList();
}
//-------------------------------------------------------------- role CRUD
/**
* Returns true if user has role specified.
*/
@Override
public boolean hasRole(String roleName, User user) throws WebloggerException {
TypedQuery<UserRole> q = strategy.getNamedQuery("UserRole.getByUserNameAndRole", UserRole.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, roleName);
try {
q.getSingleResult();
} catch (NoResultException e) {
return false;
}
return true;
}
/**
* Get all of user's roles.
*/
@Override
public List<String> getRoles(User user) throws WebloggerException {
TypedQuery<UserRole> q = strategy.getNamedQuery("UserRole.getByUserName", UserRole.class);
q.setParameter(1, user.getUserName());
List<UserRole> roles = q.getResultList();
List<String> roleNames = new ArrayList<>();
if (roles != null) {
for (UserRole userRole : roles) {
roleNames.add(userRole.getRole());
}
}
return roleNames;
}
/**
* Grant to user role specified by role name.
*/
@Override
public void grantRole(String roleName, User user) throws WebloggerException {
if (!hasRole(roleName, user)) {
UserRole role = new UserRole(user.getUserName(), roleName);
this.strategy.store(role);
}
}
@Override
public void revokeRole(String roleName, User user) throws WebloggerException {
TypedQuery<UserRole> q = strategy.getNamedQuery("UserRole.getByUserNameAndRole", UserRole.class);
q.setParameter(1, user.getUserName());
q.setParameter(2, roleName);
try {
UserRole role = q.getSingleResult();
this.strategy.remove(role);
} catch (NoResultException e) {
throw new WebloggerException("ERROR: removing role", e);
}
}
}