added oidc authentication
This commit is contained in:
@@ -9,4 +9,5 @@ module net.woggioni.jpacrepo.api {
|
||||
exports net.woggioni.jpacrepo.api.model;
|
||||
exports net.woggioni.jpacrepo.api.service;
|
||||
exports net.woggioni.jpacrepo.api.wire;
|
||||
exports net.woggioni.jpacrepo.api.security;
|
||||
}
|
@@ -0,0 +1,9 @@
|
||||
package net.woggioni.jpacrepo.api.security;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
public class Roles {
|
||||
public static final String WRITER = "jpacrepo";
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
package net.woggioni.jpacrepo.api.service;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ejb.Local;
|
||||
import net.woggioni.jpacrepo.api.model.CompressionFormat;
|
||||
import net.woggioni.jpacrepo.api.model.PkgData;
|
||||
@@ -7,6 +8,7 @@ import net.woggioni.jpacrepo.api.model.PkgData;
|
||||
import java.util.List;
|
||||
|
||||
@Local
|
||||
@PermitAll
|
||||
public interface PacmanServiceLocal extends PacmanServiceRemote {
|
||||
long countResults(String name, String version, String arch);
|
||||
List<PkgData> searchPackage(String name,
|
||||
|
@@ -2,10 +2,13 @@ package net.woggioni.jpacrepo.api.service;
|
||||
|
||||
import jakarta.annotation.Nonnull;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.ejb.Remote;
|
||||
import net.woggioni.jpacrepo.api.model.CompressionFormat;
|
||||
import net.woggioni.jpacrepo.api.model.PkgData;
|
||||
import net.woggioni.jpacrepo.api.model.PkgId;
|
||||
import net.woggioni.jpacrepo.api.security.Roles;
|
||||
import net.woggioni.jpacrepo.api.wire.PkgTuple;
|
||||
|
||||
import java.io.InputStream;
|
||||
@@ -15,6 +18,7 @@ import java.util.NavigableMap;
|
||||
import java.util.Set;
|
||||
|
||||
@Remote
|
||||
@PermitAll
|
||||
public interface PacmanServiceRemote {
|
||||
void syncDB();
|
||||
|
||||
@@ -42,9 +46,11 @@ public interface PacmanServiceRemote {
|
||||
@Nullable
|
||||
Long getFileSize(String fileName);
|
||||
|
||||
@RolesAllowed(Roles.WRITER)
|
||||
Set<String> missingFiles(Collection<String> fileNames);
|
||||
|
||||
NavigableMap<PkgId, PkgTuple> getPkgMap();
|
||||
|
||||
@RolesAllowed(Roles.WRITER)
|
||||
boolean addPackage(String fileName, InputStream inputStream);
|
||||
}
|
@@ -70,6 +70,7 @@ import java.util.stream.Stream;
|
||||
@Local({PacmanServiceLocal.class})
|
||||
@Remote({PacmanServiceRemote.class})
|
||||
public class PacmanServiceEJB implements PacmanServiceLocal {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager em;
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package net.woggioni.jpacrepo.service;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ejb.ConcurrencyManagement;
|
||||
import jakarta.ejb.ConcurrencyManagementType;
|
||||
import jakarta.ejb.TransactionAttribute;
|
||||
@@ -7,11 +8,8 @@ import jakarta.ejb.TransactionAttributeType;
|
||||
import jakarta.ejb.TransactionManagement;
|
||||
import jakarta.ejb.TransactionManagementType;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.EntityManagerFactory;
|
||||
import jakarta.persistence.NoResultException;
|
||||
import jakarta.persistence.NonUniqueResultException;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.ws.rs.BadRequestException;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.FormParam;
|
||||
@@ -31,25 +29,22 @@ import jakarta.ws.rs.core.EntityTag;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Request;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.SecurityContext;
|
||||
import jakarta.ws.rs.core.StreamingOutput;
|
||||
import jakarta.ws.rs.core.UriInfo;
|
||||
import lombok.SneakyThrows;
|
||||
import net.woggioni.jpacrepo.api.model.CompressionFormat;
|
||||
import net.woggioni.jpacrepo.api.model.PkgData;
|
||||
import net.woggioni.jpacrepo.api.model.PkgId;
|
||||
import net.woggioni.jpacrepo.api.security.Roles;
|
||||
import net.woggioni.jpacrepo.api.service.PacmanServiceLocal;
|
||||
import net.woggioni.jpacrepo.config.AppConfig;
|
||||
import net.woggioni.jpacrepo.impl.model.CompressionFormatImpl;
|
||||
import net.woggioni.jpacrepo.impl.model.PkgDataImpl;
|
||||
import net.woggioni.jpacrepo.api.wire.PkgDataList;
|
||||
import net.woggioni.jpacrepo.api.wire.PkgTuple;
|
||||
import net.woggioni.jpacrepo.api.wire.StringList;
|
||||
import net.woggioni.jpacrepo.version.PkgIdComparator;
|
||||
import net.woggioni.jpacrepo.config.AppConfig;
|
||||
import net.woggioni.jpacrepo.version.VersionComparator;
|
||||
import net.woggioni.jwo.CollectionUtils;
|
||||
import net.woggioni.jwo.Con;
|
||||
import net.woggioni.jwo.JWO;
|
||||
import net.woggioni.jwo.Tuple2;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
|
||||
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
|
||||
import org.slf4j.Logger;
|
||||
@@ -60,10 +55,9 @@ import java.net.URI;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableMap;
|
||||
@@ -72,14 +66,15 @@ import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.woggioni.jpacrepo.api.security.Roles.WRITER;
|
||||
|
||||
@Path("/pkg")
|
||||
@PermitAll
|
||||
@ConcurrencyManagement(ConcurrencyManagementType.BEAN)
|
||||
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
|
||||
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
|
||||
@TransactionManagement(TransactionManagementType.CONTAINER)
|
||||
public class PacmanWebService {
|
||||
@Inject
|
||||
private EntityManagerFactory emf;
|
||||
|
||||
@Inject
|
||||
private Logger log;
|
||||
@@ -139,7 +134,7 @@ public class PacmanWebService {
|
||||
cc.setNoCache(true);
|
||||
|
||||
NavigableMap<PkgId, PkgTuple> cachedMap = service.getPkgMap();
|
||||
EntityTag etag = new EntityTag(Integer.toString(cachedMap.hashCode()), false);
|
||||
EntityTag etag = new EntityTag(Integer.toString(cachedMap.hashCode()), true);
|
||||
Response.ResponseBuilder builder = request.evaluatePreconditions(etag);
|
||||
if (builder == null) {
|
||||
|
||||
@@ -238,8 +233,10 @@ public class PacmanWebService {
|
||||
@POST
|
||||
@Path("/doYouWantAny")
|
||||
@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
|
||||
public Response doYouWantAny(List<String> fileNames) {
|
||||
if (fileNames.isEmpty()) {
|
||||
public Response doYouWantAny(List<String> fileNames, @Context SecurityContext sctx) {
|
||||
if(!sctx.isUserInRole(WRITER)) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
} else if (fileNames.isEmpty()) {
|
||||
return Response.ok(fileNames).build();
|
||||
}
|
||||
else {
|
||||
@@ -255,9 +252,11 @@ public class PacmanWebService {
|
||||
public Response createPackage(
|
||||
InputStream input,
|
||||
@MatrixParam("filename") String filename,
|
||||
@Context UriInfo uriInfo) {
|
||||
EntityManager em = emf.createEntityManager();
|
||||
if (filename == null) throw new BadRequestException();
|
||||
@Context UriInfo uriInfo,
|
||||
@Context SecurityContext sctx) {
|
||||
if(!sctx.isUserInRole(Roles.WRITER)) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
} else if (filename == null) throw new BadRequestException();
|
||||
Response result;
|
||||
boolean added = service.addPackage(filename, input);
|
||||
if(!added) {
|
||||
@@ -334,4 +333,14 @@ public class PacmanWebService {
|
||||
};
|
||||
return Response.ok(stream).header("Content-Disposition", "attachment; filename=pkgs.tar").build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("whoAmI")
|
||||
@SneakyThrows
|
||||
public Response whoAmI(@Context SecurityContext securityContext) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("user", securityContext.getUserPrincipal().getName());
|
||||
result.put("jpacrepo", Boolean.toString(securityContext.isUserInRole("jpacrepo")));
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
}
|
||||
|
@@ -1,18 +1,23 @@
|
||||
package net.woggioni.jpacrepo.service;
|
||||
|
||||
import jakarta.annotation.security.DeclareRoles;
|
||||
import jakarta.ws.rs.ApplicationPath;
|
||||
import jakarta.ws.rs.core.Application;
|
||||
import net.woggioni.jpacrepo.api.security.Roles;
|
||||
import net.woggioni.jpacrepo.service.exception.EJBAccessExceptionMapper;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@ApplicationPath("api")
|
||||
@DeclareRoles({Roles.WRITER})
|
||||
public class WxRsApplication extends Application {
|
||||
|
||||
@Override
|
||||
public Set<Class<?>> getClasses() {
|
||||
Set<Class<?>> result = new HashSet<>();
|
||||
result.add(PacmanWebService.class);
|
||||
result.add(EJBAccessExceptionMapper.class);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,12 @@
|
||||
package net.woggioni.jpacrepo.service.exception;
|
||||
|
||||
import jakarta.ejb.EJBAccessException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.ext.ExceptionMapper;
|
||||
|
||||
public class EJBAccessExceptionMapper implements ExceptionMapper<EJBAccessException> {
|
||||
@Override
|
||||
public Response toResponse(EJBAccessException exception) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
}
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
package net.woggioni.jpacrepo.service.jpa;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.TypedQuery;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
import jakarta.persistence.criteria.CriteriaQuery;
|
||||
import jakarta.persistence.criteria.Root;
|
||||
import jakarta.persistence.criteria.Selection;
|
||||
import jakarta.persistence.metamodel.Metamodel;
|
||||
|
||||
public class QueryBuilder<T> {
|
||||
private final EntityManager em;
|
||||
private final CriteriaBuilder cb;
|
||||
private final CriteriaQuery<T> criteriaQuery;
|
||||
private final Metamodel metamodel;
|
||||
public QueryBuilder(EntityManager em, Class<T> cls) {
|
||||
this.em = em;
|
||||
this.cb = em.getCriteriaBuilder();
|
||||
this.criteriaQuery = cb.createQuery(cls);
|
||||
this.metamodel = em.getMetamodel();
|
||||
}
|
||||
|
||||
public QueryBuilder<T> select(Selection<? extends T> a) {
|
||||
criteriaQuery.select(a);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <U> Root<U> from(Class<U> cls) {
|
||||
return criteriaQuery.from(metamodel.entity(cls));
|
||||
}
|
||||
|
||||
public TypedQuery<T> build() {
|
||||
return em.createQuery(criteriaQuery);
|
||||
}
|
||||
}
|
14
src/main/webapp/WEB-INF/oidc.json
Normal file
14
src/main/webapp/WEB-INF/oidc.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"realm": "woggioni.net",
|
||||
"auth-server-url": "https://woggioni.net/auth",
|
||||
"public-client": false,
|
||||
"ssl-required": "all",
|
||||
"resource": "jpacrepo",
|
||||
"credentials": {
|
||||
"secret": "${env.JPACREPO_OIDC_CLIENT_SECRET}"
|
||||
},
|
||||
"verify-token-audience": true,
|
||||
"confidential-port": 443,
|
||||
"token-signature-algorithm": "ES512",
|
||||
"use-resource-role-mappings": true
|
||||
}
|
21
src/main/webapp/WEB-INF/web.xml
Normal file
21
src/main/webapp/WEB-INF/web.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
|
||||
version="6.0">
|
||||
<login-config>
|
||||
<auth-method>OIDC</auth-method>
|
||||
</login-config>
|
||||
<security-constraint>
|
||||
<web-resource-collection>
|
||||
<web-resource-name>login2</web-resource-name>
|
||||
<url-pattern>/api/pkg/doYouWantAny</url-pattern>
|
||||
<url-pattern>/api/pkg/upload</url-pattern>
|
||||
</web-resource-collection>
|
||||
<auth-constraint>
|
||||
<role-name>jpacrepo</role-name>
|
||||
</auth-constraint>
|
||||
</security-constraint>
|
||||
<security-role>
|
||||
<role-name>jpacrepo</role-name>
|
||||
</security-role>
|
||||
</web-app>
|
Reference in New Issue
Block a user