/* * Copyright 2015-2025 the original author or authors. * * Licensed 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 sockslib.server.manager; import com.google.common.base.Strings; import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.mongodb.client.FindIterable; import org.bson.Document; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import sockslib.utils.mongo.MongoDBUtil; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * The class <code>MongoDBBasedUserManager</code> can manage user in MongoDB. * * @author Youchao Feng * @version 1.0 * @date Aug 23, 2015 */ public class MongoDBBasedUserManager implements UserManager { private static final Logger logger = LoggerFactory.getLogger(MongoDBBasedUserManager.class); private static final String COLLECTION_NAME = "users"; private static final String USER_USERNAME_KEY = "un"; private static final String USER_PASSWORD_KEY = "pw"; private static final String MONGO_CONFIG_FILE = "classpath:mongo.properties"; private String usernameKey = USER_USERNAME_KEY; private String passwordKey = USER_PASSWORD_KEY; private String userCollectionName = COLLECTION_NAME; /** * Cache of users */ private LoadingCache<String, User> cache; /** * MongoDB util */ private MongoDBUtil mongoDBUtil; /** * Password protector */ private PasswordProtector passwordProtector; /** * Constructs a {@link MongoDBBasedUserManager} instance with no parameters. */ public MongoDBBasedUserManager() { } /** * Constructs a {@link MongoDBBasedUserManager} instance with configuration file path. * This constructor will read a specified file. * * @param configFile Configuration file path. The path support prefix "classpath:" or "file:". */ public MongoDBBasedUserManager(String configFile) { this(MongoDBConfiguration.load(configFile)); } /** * Constructs a {@link MongoDBBasedUserManager} instance. * * @param host Host of MongoDB. * @param port Port of MongoDB. * @param databaseName Database name. */ public MongoDBBasedUserManager(String host, int port, String databaseName) { this(host, port, databaseName, null, null); } /** * Constructs a {@link MongoDBBasedUserManager} instance with {@link MongoDBConfiguration}. * * @param configuration Instance of {@link MongoDBConfiguration}. */ public MongoDBBasedUserManager(MongoDBConfiguration configuration) { this(configuration.getHost(), configuration.getPort(), configuration.getDatabase(), configuration.getUsername(), configuration.getPassword()); } /** * Constructs a {@link MongoDBConfiguration} instance with some parameters. * * @param host Host of MongoDB. * @param port Port of MongoDB. * @param databaseName Database name. * @param username Username. * @param password Password. */ public MongoDBBasedUserManager(String host, int port, String databaseName, String username, String password) { this(new MongoDBUtil(host, port, databaseName, username, password)); } public MongoDBBasedUserManager(MongoDBUtil mongoDBUtil) { cache = CacheBuilder.newBuilder().maximumSize(1000).expireAfterAccess(5, TimeUnit.MINUTES).build( new CacheLoader<String, User>() { @Override public User load(String key) throws Exception { User user; user = fetchUserFromMongoDB(key); if (user == null) { return new User(); } else { return user; } } }); this.mongoDBUtil = mongoDBUtil; passwordProtector = new NonePasswordProtector(); } /** * Creates a {@link MongoDBBasedUserManager} instance with no parameters. * This method is same as <code>new MongoDBBasedUserManager("classpath:mongo.properties")</code>. * It will read a configuration file in class path named "mongo.properties". * * @return Instance of <code>MongoDBBasedUserManager</code> */ public static MongoDBBasedUserManager newDefaultUserManager() { return new MongoDBBasedUserManager(MONGO_CONFIG_FILE); } public User fetchUserFromMongoDB(final String username) { return mongoDBUtil.execute(userCollectionName, collection -> { Document document = collection.find(new Document(usernameKey, username)).first(); if (document != null) { return formUser(document); } return null; }); } @Override public void create(final User user) { user.setPassword(generateEncryptPassword(user)); mongoDBUtil.execute(userCollectionName, collection -> { collection.insertOne(new Document().append(usernameKey, user.getUsername()) .append(passwordKey, user.getPassword())); return null; }); } @Override public UserManager addUser(final String username, final String password) { final User user = new User(username, password); user.setPassword(generateEncryptPassword(user)); mongoDBUtil.execute(userCollectionName, collection -> { collection.insertOne(new Document().append(usernameKey, user.getUsername()) .append(passwordKey, user.getPassword())); return null; }); return this; } @Override public User check(String username, String password) { if (username == null || password == null) { return null; } User user = cache.getUnchecked(username); if (user == null || user.getUsername() == null || user.getPassword() == null) { return null; } String encryptPassword = generateEncryptPassword(user, password); if (user.getPassword().equals(encryptPassword)) { return user; } return null; } @Override public void delete(final String username) { mongoDBUtil.execute(userCollectionName, collection -> { collection.deleteOne(new Document(usernameKey, username)); return null; }); cache.put(username, new User()); } @Override public List<User> findAll() { return mongoDBUtil.execute(userCollectionName, collection -> { FindIterable<Document> result = collection.find(); List<User> users = new ArrayList<>(); for (Document document : result) { users.add(formUser(document)); } return users; }); } @Override public void update(final User user) { if (user == null) { throw new IllegalArgumentException("User can't null"); } if (Strings.isNullOrEmpty(user.getUsername())) { throw new IllegalArgumentException("Username of the user can't be null or empty"); } User old = find(user.getUsername()); String newEncryptPassword = generateEncryptPassword(user); if (!old.getPassword().equals(newEncryptPassword)) { user.setPassword(newEncryptPassword); } mongoDBUtil.execute(userCollectionName, collection -> { collection.updateOne(new Document(usernameKey, user.getUsername()), new Document("$set", new Document(usernameKey, user.getPassword()))); return null; }); cache.put(user.getUsername(), user); } @Override public User find(String username) { User user = cache.getUnchecked(username); if (user.getUsername() == null) { return null; } return user; } private User formUser(Document document) { User user = new User(); user.setUsername(document.getString(usernameKey)); user.setPassword(document.getString(passwordKey)); return user; } public LoadingCache<String, User> getCache() { return cache; } public void setCache(LoadingCache<String, User> cache) { this.cache = cache; } public MongoDBUtil getMongoDBUtil() { return mongoDBUtil; } public void setMongoDBUtil(MongoDBUtil mongoDBUtil) { this.mongoDBUtil = mongoDBUtil; } public PasswordProtector getPasswordProtector() { return passwordProtector; } public void setPasswordProtector(PasswordProtector passwordProtector) { this.passwordProtector = passwordProtector; } private String generateEncryptPassword(final User user, String newPassword) { User tempUser = user.copy(); if (newPassword != null) { tempUser.setPassword(newPassword); } if (passwordProtector == null) { passwordProtector = new NonePasswordProtector(); } return passwordProtector.encrypt(tempUser); } private String generateEncryptPassword(final User user) { return generateEncryptPassword(user, null); } public String getUserCollectionName() { return userCollectionName; } public void setUserCollectionName(String userCollectionName) { this.userCollectionName = userCollectionName; } public String getUsernameKey() { return usernameKey; } public void setUsernameKey(String usernameKey) { this.usernameKey = usernameKey; } public String getPasswordKey() { return passwordKey; } public void setPasswordKey(String passwordKey) { this.passwordKey = passwordKey; } }