Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*-
* ========================LICENSE_START=================================
* restheart-security
* %%
* Copyright (C) 2018 - 2024 SoftInstigate
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see .
* =========================LICENSE_END==================================
*/
package org.restheart.security.tokens;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Map;
import org.bson.BsonDocument;
import org.restheart.cache.Cache;
import org.restheart.cache.CacheFactory;
import org.restheart.configuration.ConfigurationException;
import org.restheart.exchange.JsonProxyRequest;
import org.restheart.plugins.Inject;
import org.restheart.plugins.OnInit;
import org.restheart.plugins.RegisterPlugin;
import org.restheart.plugins.security.TokenManager;
import org.restheart.security.FileRealmAccount;
import org.restheart.security.JwtAccount;
import org.restheart.security.MongoRealmAccount;
import org.restheart.security.PwdCredentialAccount;
import org.restheart.utils.BsonUtils;
import org.restheart.utils.URLUtils;
import com.google.common.collect.Sets;
import io.undertow.security.idm.Account;
import io.undertow.security.idm.Credential;
import io.undertow.security.idm.PasswordCredential;
import io.undertow.server.HttpServerExchange;
@RegisterPlugin(name = "rndTokenManager",
description = "generates random auth tokens",
enabledByDefault = false)
public class RndTokenManager implements TokenManager {
private static final SecureRandom RND_GENERATOR = new SecureRandom();
private static Cache CACHE = null;
private int ttl = -1;
private String srvURI = null;
@Inject("config")
private Map config;
@OnInit
public void init() throws ConfigurationException {
this.ttl = arg(config, "ttl");
this.srvURI = arg(config, "srv-uri");
CACHE = CacheFactory.createLocalCache(Long.MAX_VALUE, Cache.EXPIRE_POLICY.AFTER_READ, ttl * 60 * 1_000);
}
@Override
public Account verify(final Account account) {
return account;
}
@Override
public Account verify(final String id, final Credential credential) {
final var _account = CACHE.get(id);
return _account != null && _account.isPresent() && verifyToken(_account.get(), credential)
? _account.get()
: null;
}
@Override
public Account verify(final Credential credential) {
return null;
}
private boolean verifyToken(final PwdCredentialAccount account, final Credential credential) {
if (credential instanceof PasswordCredential passwordCredential) {
char[] token = passwordCredential.getPassword();
char[] expectedToken = account.getCredentials().getPassword();
return Arrays.equals(token, expectedToken);
} else {
return false;
}
}
public Cache getCACHE() {
return CACHE;
}
@Override
public PasswordCredential get(Account account) {
var cachedAccount = CACHE.get(account.getPrincipal().getName());
if (cachedAccount != null && cachedAccount.isPresent()) {
return cachedAccount.get().getCredentials();
} else {
var newCachedTokenAccount = cloneWithToken(account, nextToken());
CACHE.put(account.getPrincipal().getName(), newCachedTokenAccount);
return newCachedTokenAccount.getCredentials();
}
}
private PwdCredentialAccount cloneWithToken(Account account, char[] token) {
PwdCredentialAccount ret;
switch (account) {
case MongoRealmAccount maccount -> ret = new MongoRealmAccount(maccount.getPrincipal().getName(), token, Sets.newTreeSet(maccount.getRoles()), maccount.properties());
case FileRealmAccount faccount -> ret = new FileRealmAccount(faccount.getPrincipal().getName(), token, Sets.newTreeSet(faccount.getRoles()), faccount.properties());
case JwtAccount jwtAccount -> {
var accountDocument = BsonUtils.parse(jwtAccount.properties());
if (accountDocument instanceof BsonDocument bad) {
ret = new MongoRealmAccount(jwtAccount.getPrincipal().getName(), token, Sets.newTreeSet(jwtAccount.getRoles()), bad);
} else {
ret = new PwdCredentialAccount(jwtAccount.getPrincipal().getName(), token, Sets.newTreeSet(jwtAccount.getRoles()));
}
}
default -> ret = new PwdCredentialAccount(account.getPrincipal().getName(), token, Sets.newTreeSet(account.getRoles()));
}
return ret;
}
@Override
public void invalidate(Account account) {
CACHE.invalidate(account.getPrincipal().getName());
}
@Override
public void update(Account account) {
String id = account.getPrincipal().getName();
var _authTokenAccount = CACHE.get(id);
if (_authTokenAccount != null && _authTokenAccount.isPresent()) {
var authTokenAccount = _authTokenAccount.get();
var updatedAuthTokenAccount = cloneWithToken(account, authTokenAccount.getCredentials().getPassword());
CACHE.put(id, updatedAuthTokenAccount);
}
}
@Override
public void injectTokenHeaders(HttpServerExchange exchange, PasswordCredential token) {
exchange.getResponseHeaders().add(AUTH_TOKEN_HEADER, new String(token.getPassword()));
exchange.getResponseHeaders().add(AUTH_TOKEN_VALID_HEADER, Instant.now().plus(ttl, ChronoUnit.MINUTES).toString());
var request = JsonProxyRequest.of(exchange);
if (request.getAuthenticatedAccount() != null
&& request.getAuthenticatedAccount().getPrincipal() != null
&& request.getAuthenticatedAccount().getPrincipal().getName() != null) {
var cid = request.getAuthenticatedAccount().getPrincipal().getName();
exchange.getResponseHeaders().add(AUTH_TOKEN_LOCATION_HEADER, URLUtils.removeTrailingSlashes(srvURI).concat("/").concat(cid));
}
}
private static char[] nextToken() {
return new BigInteger(256, RND_GENERATOR).toString(Character.MAX_RADIX).toCharArray();
}
}