com.github.bjoernpetersen.jmusicbot.user.UserManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of musicbot Show documentation
Show all versions of musicbot Show documentation
Core library of MusicBot, which plays music from various providers.
package com.github.bjoernpetersen.jmusicbot.user;
import com.github.bjoernpetersen.jmusicbot.config.Config;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacSigner;
import java.io.Closeable;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.sql.SQLException;
import java.time.Instant;
import java.time.Period;
import java.util.Base64;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nonnull;
import org.mindrot.jbcrypt.BCrypt;
public final class UserManager implements Closeable {
private static final String BOT_NAME = "MusicBot";
@Nonnull
private final Config.StringEntry signatureKey;
@Nonnull
private final String guestSignatureKey;
@Nonnull
private final Database database;
@Nonnull
private final Map temporaryUsers;
@Nonnull
private final LoadingCache users;
@Nonnull
private final User botUser;
public UserManager(@Nonnull Config config, @Nonnull String databaseUrl) throws SQLException {
this.signatureKey = config.new StringEntry(getClass(), "signatureKey", "", true);
this.guestSignatureKey = createSignatureKey();
this.database = new Database(databaseUrl);
this.temporaryUsers = new HashMap<>(32);
this.users = CacheBuilder.newBuilder()
.maximumSize(64)
.build(new CacheLoader() {
@Override
public User load(@Nonnull String name) throws Exception {
User user = temporaryUsers.get(name.toLowerCase());
return user == null ? database.getUser(name) : user;
}
});
this.botUser = new User(BOT_NAME, UUID.randomUUID().toString());
}
/**
* Gets the bot user. This user is dedicated to perform all your admin needs.
*
* The ID of this bot may change over time.
*
* @return a User
*/
@Nonnull
public User getBotUser() {
return botUser;
}
@Nonnull
public User createTemporaryUser(String name, String uuid) throws DuplicateUserException {
if (BOT_NAME.equalsIgnoreCase(name)) {
throw new DuplicateUserException("Invalid username");
}
try {
// TODO create "hasUser" method in Database
getUser(name);
throw new DuplicateUserException("User already exists: " + name);
} catch (UserNotFoundException expected) {
User user = new User(name, uuid);
temporaryUsers.put(name.toLowerCase(), user);
return user;
}
}
@Nonnull
public User getUser(String name) throws UserNotFoundException {
User user;
if (BOT_NAME.equals(name)) {
return botUser;
}
try {
user = users.get(name);
} catch (ExecutionException e) {
throw (UserNotFoundException) e.getCause();
}
if (user.isInvalid()) {
users.invalidate(user);
return getUser(name);
} else {
return user;
}
}
/**
* Updates the specified users password.
*
* If the user is a guest, this will make them a full user.
*
* @param user the user name
* @param password the new password
* @return a new, valid user object
* @throws DuplicateUserException if the specified user was a guest and a full user with the same
* name already existed
* @throws SQLException if any SQL errors occur
*/
@Nonnull
public User updateUser(User user, String password) throws DuplicateUserException, SQLException {
if (user.isInvalid() || password.isEmpty()) {
throw new IllegalArgumentException();
}
String name = user.getName();
User newUser;
if (user.isTemporary()) {
newUser = database.createUser(user.getName(), hash(password));
temporaryUsers.remove(name.toLowerCase());
} else {
newUser = database.updatePassword(user, hash(password));
}
users.put(name, newUser);
return newUser;
}
@Nonnull
public User updateUser(User user, Set permissions) throws SQLException {
if (user.isInvalid() || user.isTemporary()) {
throw new IllegalArgumentException();
}
User newUser = database.updatePermissions(user, permissions);
users.put(newUser.getName(), newUser);
return newUser;
}
public void deleteUser(User user) throws SQLException {
String name = user.getName();
database.dropUser(user);
users.invalidate(name);
}
/**
* Gets all full users.
*
* @return a list of users
* @throws SQLException if any SQL errors occur
*/
public List getUsers() throws SQLException {
return database.getUsers();
}
@Nonnull
public String toToken(User user) {
if (user.isInvalid()) {
throw new IllegalArgumentException();
}
String signatureKey;
if (user.isTemporary()) {
signatureKey = this.guestSignatureKey;
} else {
signatureKey = getSignatureKey();
}
JwtBuilder builder = Jwts.builder()
.setSubject(user.getName())
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS512, signatureKey)
.setExpiration(Date.from(Instant.now().plus(Period.ofDays(7))));
for (Permission permission : user.getPermissions()) {
builder.claim(permission.getLabel(), true);
}
return builder.compact();
}
@Nonnull
public User fromToken(String token) throws InvalidTokenException {
Jws parsed;
try {
parsed = Jwts.parser()
.setSigningKey(getSignatureKey())
.parseClaimsJws(token);
} catch (JwtException e) {
// try again with guest key
try {
parsed = Jwts.parser()
.setSigningKey(guestSignatureKey)
.parseClaimsJws(token);
} catch (JwtException e1) {
e1.addSuppressed(e);
throw new InvalidTokenException(e1);
}
}
String name = parsed.getBody().getSubject();
if (name == null) {
throw new InvalidTokenException("Name missing");
}
try {
return getUser(name);
} catch (UserNotFoundException e) {
throw new InvalidTokenException(e);
}
}
@Nonnull
private String getSignatureKey() {
String configKey = signatureKey.getValue();
if (configKey != null) {
return configKey;
} else {
String key = createSignatureKey();
signatureKey.set(key);
return key;
}
}
@Nonnull
private String createSignatureKey() {
byte[] encoded = Base64.getEncoder().encode(MacSigner.generateKey().getEncoded());
return new String(encoded, StandardCharsets.UTF_8);
}
@Nonnull
private String hash(String password) {
return BCrypt.hashpw(password, BCrypt.gensalt());
}
@Override
public void close() throws IOException {
signatureKey.destruct();
database.close();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy