CtanAuthFilter.java
/*
* Copyright © 2024-2025 The CTAN Team and individual authors
*
* This file is distributed under the 3-clause BSD license.
* See file LICENSE for details.
*/
package org.ctan.site;
import java.io.IOException;
import java.security.Principal;
import java.util.Set;
import org.ctan.site.domain.account.Role;
import org.ctan.site.services.account.JwtManager;
import org.ctan.site.stores.UserStore;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.dropwizard.hibernate.UnitOfWork;
import jakarta.annotation.Priority;
import jakarta.ws.rs.Priorities;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.container.ContainerRequestFilter;
import jakarta.ws.rs.core.SecurityContext;
import lombok.Builder;
import lombok.NonNull;
/**
* The class <code>CtanAuthFilter</code> contains the filter for checking the
* authentication.
*/
@Priority(Priorities.AUTHENTICATION)
public class CtanAuthFilter implements ContainerRequestFilter {
/**
* The class <code>CtanSecurityContext</code> contains the joined security
* context and principal.
*/
@Builder
@SuppressFBWarnings(value = "EI_EXPOSE_REP2")
private static final class CtanSecurityContext
implements
SecurityContext,
Principal {
/**
* The field <code>account</code> contains the account name of the user.
*/
private String account;
/**
* The field <code>roles</code> contains the set of roles of the user.
*/
private Set<Role> roles;
/**
* This is the constructor for <code>CtanSecurityContext</code>.
*
* @param account the account
* @param roles the roles
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP2")
public CtanSecurityContext(String account, Set<Role> roles) {
this.account = account;
this.roles = roles;
}
/**
* {@inheritDoc}
*
* @see jakarta.ws.rs.core.SecurityContext#getAuthenticationScheme()
*/
@Override
public String getAuthenticationScheme() {
return SecurityContext.BASIC_AUTH;
}
/**
* {@inheritDoc}
*
* @see java.security.Principal#getName()
*/
@Override
public String getName() {
return account;
}
/**
* {@inheritDoc}
*
* @see jakarta.ws.rs.core.SecurityContext#getUserPrincipal()
*/
@Override
public Principal getUserPrincipal() {
return this;
}
/**
* {@inheritDoc}
*
* @see jakarta.ws.rs.core.SecurityContext#isSecure()
*/
@Override
public boolean isSecure() {
return true;
}
/**
* {@inheritDoc}
*
* @see jakarta.ws.rs.core.SecurityContext#isUserInRole(java.lang.String)
*/
@Override
public boolean isUserInRole(String role) {
if (role == null) {
return false;
}
for (Role r : roles) {
if (role.equals(r.toString())) {
return true;
}
}
return false;
}
}
/**
* The field <code>userStore</code> contains the user store.
*/
private UserStore userStore;
/**
* This is the constructor for <code>CtanAuthFilter</code>.
*
* @param userStore the user store
*/
@SuppressFBWarnings(value = "EI_EXPOSE_REP2")
public CtanAuthFilter(UserStore userStore) {
this.userStore = userStore;
}
/**
* {@inheritDoc}
*
* @see jakarta.ws.rs.container.ContainerRequestFilter#filter(
* jakarta.ws.rs.container.ContainerRequestContext)
*/
@Override
@UnitOfWork(value = "siteDb")
public void filter(@NonNull ContainerRequestContext requestContext)
throws IOException {
var jwt = requestContext.getHeaderString("Authentication");
if (jwt == null) {
return;
}
var account = JwtManager.verifyAuth(jwt);
if (account == null) {
return;
}
var user = userStore.getByAccount(account);
if (user == null) {
return;
}
requestContext
.setSecurityContext(
new CtanSecurityContext(account, user.getRoles()));
}
}