All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.bjoernpetersen.jmusicbot.user.UserManager Maven / Gradle / Ivy

There is a newer version: 0.25.0
Show newest version
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