package io.vertx.ext.auth.oauth2.impl;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.auth.AbstractUser;
import io.vertx.ext.auth.AuthProvider;
import io.vertx.ext.auth.oauth2.AccessToken;
import io.vertx.ext.auth.oauth2.OAuth2Auth;
import io.vertx.ext.auth.oauth2.OAuth2RBAC;
import io.vertx.ext.jwt.JWT;
import io.vertx.ext.jwt.JWTOptions;
import java.util.Base64;
import java.util.regex.Pattern;
public abstract class OAuth2UserImpl extends AbstractUser implements AccessToken {
private static final Logger LOG = LoggerFactory.getLogger(OAuth2UserImpl.class);
private JsonObject principal;
private transient OAuth2AuthProviderImpl provider;
private transient OAuth2RBAC rbac;
protected transient JsonObject accessToken;
protected transient JsonObject refreshToken;
protected transient JsonObject idToken;
public OAuth2UserImpl() {
}
public OAuth2UserImpl(OAuth2Auth provider, JsonObject principal) {
this.principal = principal;
setAuthProvider(provider);
}
protected void init(JsonObject json) {
clearCache();
principal = json;
if (principal != null) {
if (!principal.containsKey("expires_at") && principal.containsKey("expires_in")) {
Long expiresIn;
try {
expiresIn = principal.getLong("expires_in");
} catch (ClassCastException e) {
expiresIn = Long.valueOf(principal.getString("expires_in"));
}
principal.put("expires_at", System.currentTimeMillis() + 1000 * expiresIn);
}
if (provider != null) {
accessToken = decodeToken("access_token");
if (!principal.containsKey("expires_at") && accessToken != null) {
Long exp = accessToken.getLong("exp");
if (exp != null) {
principal.put("expires_at", exp * 1000);
}
}
refreshToken = decodeToken("refresh_token", true);
idToken = decodeToken("id_token");
String scope = principal.getString("scope");
if (scope != null) {
for (String authority : scope.split(Pattern.quote(provider.getConfig().getScopeSeparator()))) {
cachePermission(authority);
}
}
}
}
}
@Override
public void setAuthProvider(AuthProvider authProvider) {
provider = (OAuth2AuthProviderImpl) authProvider;
rbac = provider.getRBACHandler();
init(principal);
}
protected OAuth2AuthProviderImpl getProvider() {
return provider;
}
@Override
public JsonObject principal() {
return principal;
}
@Override
protected void doIsPermitted(String permission, Handler<AsyncResult<Boolean>> resultHandler) {
if (expired()) {
resultHandler.handle(Future.failedFuture("Expired Token"));
return;
}
if (rbac == null) {
resultHandler.handle(Future.failedFuture("No RBAC Handler available"));
} else {
rbac.isAuthorized(this, permission, resultHandler);
}
}
@Override
public void writeToBuffer(Buffer buff) {
super.writeToBuffer(buff);
if (principal != null) {
Buffer buffer = principal.toBuffer();
buff.appendInt(buffer.length());
buff.appendBuffer(buffer);
} else {
buff.appendInt(0);
}
}
@Override
public int readFromBuffer(int pos, Buffer buff) {
pos = super.readFromBuffer(pos, buff);
int len = buff.getInt(pos);
pos += 4;
if (len > 0) {
Buffer buffer = buff.getBuffer(pos, pos + len);
principal = new JsonObject(buffer);
pos += len;
} else {
principal.clear();
}
init(principal);
return pos;
}
protected JsonObject decodeToken(String tokenType) {
return decodeToken(tokenType, false);
}
protected JsonObject decodeToken(String tokenType, boolean trust) {
final Object opaque = principal.getValue(tokenType);
if (opaque == null) {
return null;
}
if (opaque instanceof JsonObject) {
return (JsonObject) opaque;
}
try {
if (trust) {
String[] segments = ((String) opaque).split("\\.");
if (segments.length == 2 || segments.length == 3) {
String payloadSeg = segments[1];
return new JsonObject(Buffer.buffer(Base64.getUrlDecoder().decode(payloadSeg)));
}
} else {
return provider.getJWT().decode(((String) opaque));
}
} catch (RuntimeException e) {
LOG.debug("Cannot decode token:", e);
}
return null;
}
@Override
public String opaqueAccessToken() {
return principal.getString("access_token");
}
@Override
public String opaqueRefreshToken() {
return principal.getString("refresh_token");
}
@Override
public String opaqueIdToken() {
return principal.getString("id_token");
}
@Override
public JsonObject accessToken() {
if (accessToken != null) {
return accessToken.copy();
}
return null;
}
@Override
@Deprecated
public JsonObject refreshToken() {
if (refreshToken != null) {
return refreshToken.copy();
}
return null;
}
@Override
public JsonObject idToken() {
if (idToken != null) {
return idToken.copy();
}
return null;
}
@Override
public boolean expired() {
if (principal == null) {
return true;
}
if (accessToken != null) {
final JWT jwt = provider.getJWT();
final JWTOptions options = provider.getConfig().getJWTOptions();
try {
jwt.isExpired(accessToken, options);
} catch (RuntimeException e) {
LOG.debug("Expired token:", e);
return true;
}
}
long now = System.currentTimeMillis();
if (principal.containsKey("expires_at") && principal.getLong("expires_at", 0L) < now) {
return true;
}
return false;
}
}