Compare commits
35 Commits
820cf7d3d2
...
b6464b7962
Author | SHA1 | Date | |
---|---|---|---|
b6464b7962 | |||
83977e060c | |||
359f235274 | |||
b0d565b014 | |||
fe84af5d96 | |||
add346cf29 | |||
8d63a758f7 | |||
b9eb17e5c6 | |||
6d05b3444c | |||
|
7b5e034ac2 | ||
|
b4554a8bdb | ||
|
ae84d1c6c8 | ||
239763cf48 | |||
754c422be0 | |||
091ac4babd | |||
dccb6b80d5 | |||
5633604d13 | |||
96cb8a6e16 | |||
ebe88e4a8d | |||
c82ab9f27f | |||
f914462460 | |||
9da5f589db | |||
a0a35efeaf | |||
abf1ddcf24 | |||
3bbbea87fa | |||
25a163d4fa | |||
c9b9d38efe | |||
cd3a6a1d8b | |||
|
5c1b7cd193 | ||
|
9ed09d4fed | ||
|
33665d47b8 | ||
|
b907d2212a | ||
a0f4680271 | |||
d9e118afaa | |||
9f43ebc782 |
16
.github/workflows/assign-pr-author.yml
vendored
Normal file
16
.github/workflows/assign-pr-author.yml
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
name: "Assign PR Author as Assignee"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
|
||||
jobs:
|
||||
assign-pr-author-as-assignee:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Assign Author as Assignee"
|
||||
uses: itsOliverBott/assign-pr-author-as-assignee@latest
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
33
.github/workflows/check-title.yml
vendored
Normal file
33
.github/workflows/check-title.yml
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
---
|
||||
name: "Check PR title"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- synchronize
|
||||
- ready_for_review
|
||||
- reopened
|
||||
|
||||
jobs:
|
||||
check-title:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Check title"
|
||||
uses: Slashgear/action-check-pr-title@v4.3.0
|
||||
with:
|
||||
regexp: "\\[(API,)?(API|DEV-OPS|DOC|FEAT|FIX|FIX\\-CI|STYLE)\\]( \\([A-Za-z0-9.\\-]+\\))? [A-Za-z0-9 ,.'\\-!]+$"
|
||||
helpMessage: |
|
||||
Title of the PR MUST respect format: "[{TYPE}] clear description without typos in english" with {TYPE}:
|
||||
* [API] Change API that permit to access on the application (un-compatibility only). This one can specifically added with [API,{TYPE}]
|
||||
* [DEV-OPS] Update automatic build system, method to deliver application/packages, ...
|
||||
* [DOC] Update or add some documentation.
|
||||
* [FEAT] Develop a new feature
|
||||
* [FIX] When fixing issue
|
||||
* [FIX-CI] When the CI fail to build and we apply a correction to set it work again.
|
||||
* [STYLE] Update of the style tools/checker, or add/remove rules.
|
||||
Examples:
|
||||
[FEAT] My beautiful feature
|
||||
[API,FIX] Change API to fix typo
|
||||
[FIX] (module) Correct part of ...
|
12
.github/workflows/maven.yml
vendored
12
.github/workflows/maven.yml
vendored
@ -10,19 +10,17 @@ name: Java CI with Maven
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "develop" ]
|
||||
branches:
|
||||
- develop
|
||||
pull_request:
|
||||
branches: [ "develop" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v3
|
||||
uses: actions/setup-java@v4
|
||||
with:
|
||||
java-version: '21'
|
||||
distribution: 'temurin'
|
||||
@ -34,4 +32,4 @@ jobs:
|
||||
|
||||
# Optional: Uploads the full dependency graph to GitHub to improve the quality of Dependabot alerts this repository can receive
|
||||
- name: Update dependency graph
|
||||
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6
|
||||
uses: advanced-security/maven-dependency-submission-action@4f64ddab9d742a4806eeb588d238e4c311a8397d
|
||||
|
2
pom.xml
2
pom.xml
@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>kangaroo-and-rabbit</groupId>
|
||||
<artifactId>archidata</artifactId>
|
||||
<version>0.14.3-SNAPSHOT</version>
|
||||
<version>0.19.1-SNAPSHOT</version>
|
||||
<properties>
|
||||
<java.version>21</java.version>
|
||||
<maven.compiler.version>3.1</maven.compiler.version>
|
||||
|
@ -23,6 +23,7 @@ import jakarta.persistence.ManyToMany;
|
||||
import jakarta.persistence.ManyToOne;
|
||||
import jakarta.persistence.OneToMany;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.Email;
|
||||
import jakarta.validation.constraints.Max;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
@ -83,27 +84,19 @@ public class AnnotationTools {
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public static boolean getSchemaReadOnly(final Field element) throws DataAccessException {
|
||||
public static boolean getSchemaReadOnly(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
|
||||
if (annotation.length == 0) {
|
||||
return false;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Schema) annotation[0]).readOnly();
|
||||
}
|
||||
|
||||
public static String getSchemaExample(final Class<?> element) throws DataAccessException {
|
||||
public static String getSchemaExample(final Class<?> element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Schema) annotation[0]).example();
|
||||
}
|
||||
|
||||
@ -115,51 +108,27 @@ public class AnnotationTools {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static String getSchemaDescription(final Class<?> element) throws DataAccessException {
|
||||
public static String getSchemaDescription(final Class<?> element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Schema) annotation[0]).description();
|
||||
}
|
||||
|
||||
public static String getSchemaDescription(final Field element) throws DataAccessException {
|
||||
public static String getSchemaDescription(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Schema.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Schema on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Schema) annotation[0]).description();
|
||||
}
|
||||
|
||||
public static String getComment(final Field element) throws DataAccessException {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataComment.class);
|
||||
if (annotation.length == 0) {
|
||||
return getSchemaDescription(element);
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @DataComment on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((DataComment) annotation[0]).value();
|
||||
}
|
||||
|
||||
public static String getDefault(final Field element) throws DataAccessException {
|
||||
public static String getDefault(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DefaultValue.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @DataDefault on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((DefaultValue) annotation[0]).value();
|
||||
}
|
||||
|
||||
@ -187,79 +156,63 @@ public class AnnotationTools {
|
||||
return (OneToMany) annotation[0];
|
||||
}
|
||||
|
||||
public static DataJson getDataJson(final Field element) throws DataAccessException {
|
||||
public static DataJson getDataJson(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(DataJson.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @ManyToOne on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return (DataJson) annotation[0];
|
||||
}
|
||||
|
||||
public static Long getConstraintsMax(final Field element) throws DataAccessException {
|
||||
public static Long getConstraintsMax(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Max.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Max) annotation[0]).value();
|
||||
}
|
||||
|
||||
public static Long getConstraintsMin(final Field element) throws DataAccessException {
|
||||
public static Long getConstraintsMin(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Min.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Min) annotation[0]).value();
|
||||
}
|
||||
|
||||
public static int getLimitSize(final Field element) throws DataAccessException {
|
||||
public static int getLimitSize(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
|
||||
if (annotation.length == 0) {
|
||||
return 255;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
final int length = ((Column) annotation[0]).length();
|
||||
return length <= 0 ? 0 : length;
|
||||
}
|
||||
|
||||
public static Size getConstraintsSize(final Field element) throws DataAccessException {
|
||||
public static Size getConstraintsSize(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Size.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Size on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return (Size) annotation[0];
|
||||
}
|
||||
|
||||
public static String getConstraintsPattern(final Field element) throws DataAccessException {
|
||||
public static String getConstraintsPattern(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Pattern.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Pattern on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Pattern) annotation[0]).regexp();
|
||||
}
|
||||
|
||||
public static boolean getConstraintsEmail(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Email.class);
|
||||
if (annotation.length == 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isAnnotationGroup(final Field field, final Class<?> annotationType) {
|
||||
try {
|
||||
final Annotation[] anns = field.getAnnotations();
|
||||
@ -295,19 +248,15 @@ public class AnnotationTools {
|
||||
return name;
|
||||
}
|
||||
|
||||
public static boolean getColumnNotNull(final Field element) throws DataAccessException {
|
||||
public static boolean getColumnNotNull(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
|
||||
if (annotation.length == 0) {
|
||||
return false;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return !((Column) annotation[0]).nullable();
|
||||
}
|
||||
|
||||
public static boolean getNullable(final Field element) throws DataAccessException {
|
||||
public static boolean getNullable(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Nullable.class);
|
||||
if (annotation.length == 0) {
|
||||
return false;
|
||||
@ -315,19 +264,15 @@ public class AnnotationTools {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean getConstraintsNotNull(final Field element) throws DataAccessException {
|
||||
public static boolean getConstraintsNotNull(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(NotNull.class);
|
||||
if (annotation.length == 0) {
|
||||
return false;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @NotNull on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Field getPrimaryKeyField(final Class<?> clazz) throws DataAccessException {
|
||||
public static Field getPrimaryKeyField(final Class<?> clazz) {
|
||||
for (final Field field : clazz.getFields()) {
|
||||
// static field is only for internal global declaration ==> remove it ..
|
||||
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
|
||||
@ -340,7 +285,7 @@ public class AnnotationTools {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean isPrimaryKey(final Field element) throws DataAccessException {
|
||||
public static boolean isPrimaryKey(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Id.class);
|
||||
if (annotation.length == 0) {
|
||||
return false;
|
||||
@ -348,51 +293,43 @@ public class AnnotationTools {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean isUnique(final Field element) throws DataAccessException {
|
||||
public static boolean isUnique(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(Column.class);
|
||||
if (annotation.length == 0) {
|
||||
return false;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((Column) annotation[0]).unique();
|
||||
}
|
||||
|
||||
public static GenerationType getStrategy(final Field element) throws DataAccessException {
|
||||
public static GenerationType getStrategy(final Field element) {
|
||||
final Annotation[] annotation = element.getDeclaredAnnotationsByType(GeneratedValue.class);
|
||||
if (annotation.length == 0) {
|
||||
return null;
|
||||
}
|
||||
if (annotation.length > 1) {
|
||||
throw new DataAccessException(
|
||||
"Must not have more than 1 element @Column on " + element.getClass().getCanonicalName());
|
||||
}
|
||||
return ((GeneratedValue) annotation[0]).strategy();
|
||||
}
|
||||
|
||||
public static boolean isDeletedField(final Field element) throws DataAccessException {
|
||||
public static boolean isDeletedField(final Field element) {
|
||||
return element.getDeclaredAnnotationsByType(DataDeleted.class).length != 0;
|
||||
}
|
||||
|
||||
public static boolean isCreatedAtField(final Field element) throws DataAccessException {
|
||||
public static boolean isCreatedAtField(final Field element) {
|
||||
return element.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
|
||||
}
|
||||
|
||||
public static boolean isUpdateAtField(final Field element) throws DataAccessException {
|
||||
public static boolean isUpdateAtField(final Field element) {
|
||||
return element.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
|
||||
}
|
||||
|
||||
public static boolean isdefaultNotRead(final Field element) throws DataAccessException {
|
||||
public static boolean isdefaultNotRead(final Field element) {
|
||||
return element.getDeclaredAnnotationsByType(DataNotRead.class).length != 0;
|
||||
}
|
||||
|
||||
public static boolean isIdField(final Field element) throws DataAccessException {
|
||||
public static boolean isIdField(final Field element) {
|
||||
return element.getDeclaredAnnotationsByType(Id.class).length != 0;
|
||||
}
|
||||
|
||||
public static String getDeletedFieldName(final Class<?> clazz) throws DataAccessException {
|
||||
public static String getDeletedFieldName(final Class<?> clazz) {
|
||||
try {
|
||||
for (final Field elem : clazz.getFields()) {
|
||||
// static field is only for internal global declaration ==> remove it ..
|
||||
@ -409,7 +346,7 @@ public class AnnotationTools {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String getUpdatedFieldName(final Class<?> clazz) throws DataAccessException {
|
||||
public static String getUpdatedFieldName(final Class<?> clazz) {
|
||||
try {
|
||||
for (final Field elem : clazz.getFields()) {
|
||||
// static field is only for internal global declaration ==> remove it ..
|
||||
@ -443,16 +380,15 @@ public class AnnotationTools {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static List<String> getFieldsNames(final Class<?> clazz) throws DataAccessException {
|
||||
public static List<String> getFieldsNames(final Class<?> clazz) {
|
||||
return getFieldsNamesFilter(clazz, false);
|
||||
}
|
||||
|
||||
public static List<String> getAllFieldsNames(final Class<?> clazz) throws DataAccessException {
|
||||
public static List<String> getAllFieldsNames(final Class<?> clazz) {
|
||||
return getFieldsNamesFilter(clazz, true);
|
||||
}
|
||||
|
||||
private static List<String> getFieldsNamesFilter(final Class<?> clazz, final boolean full)
|
||||
throws DataAccessException {
|
||||
private static List<String> getFieldsNamesFilter(final Class<?> clazz, final boolean full) {
|
||||
final List<String> out = new ArrayList<>();
|
||||
for (final Field field : clazz.getFields()) {
|
||||
// static field is only for internal global declaration ==> remove it ..
|
||||
@ -467,12 +403,12 @@ public class AnnotationTools {
|
||||
return out;
|
||||
}
|
||||
|
||||
public static boolean isGenericField(final Field elem) throws DataAccessException {
|
||||
public static boolean isGenericField(final Field elem) {
|
||||
return AnnotationTools.isPrimaryKey(elem) || AnnotationTools.isCreatedAtField(elem)
|
||||
|| AnnotationTools.isUpdateAtField(elem) || AnnotationTools.isDeletedField(elem);
|
||||
}
|
||||
|
||||
public static Field getFieldOfId(final Class<?> clazz) throws DataAccessException {
|
||||
public static Field getFieldOfId(final Class<?> clazz) {
|
||||
for (final Field field : clazz.getFields()) {
|
||||
// static field is only for internal global declaration ==> remove it ..
|
||||
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
|
||||
@ -485,7 +421,7 @@ public class AnnotationTools {
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Field getFieldNamed(final Class<?> clazz, final String name) throws DataAccessException {
|
||||
public static Field getFieldNamed(final Class<?> clazz, final String name) {
|
||||
for (final Field field : clazz.getFields()) {
|
||||
// static field is only for internal global declaration ==> remove it ..
|
||||
if (java.lang.reflect.Modifier.isStatic(field.getModifiers())) {
|
||||
|
@ -1,13 +0,0 @@
|
||||
package org.kar.archidata.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Target({ ElementType.TYPE, ElementType.FIELD })
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Deprecated(since = "0.5.2")
|
||||
public @interface DataComment {
|
||||
String value();
|
||||
}
|
@ -56,7 +56,7 @@ import jakarta.ws.rs.core.StreamingOutput;
|
||||
// https://stackoverflow.com/questions/35367113/jersey-webservice-scalable-approach-to-download-file-and-reply-to-client
|
||||
// https://gist.github.com/aitoroses/4f7a2b197b732a6a691d
|
||||
|
||||
// TODO: must be inherited and set the default dataAccess interface @Path("/data")
|
||||
@Path("/data")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public class DataResource {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(DataResource.class);
|
||||
@ -478,7 +478,7 @@ public class DataResource {
|
||||
to = file.length() - 1;
|
||||
}
|
||||
final String responseRange = String.format("bytes %d-%d/%d", from, to, file.length());
|
||||
// logger.info("responseRange: {}", responseRange);
|
||||
// LOGGER.info("responseRange: {}", responseRange);
|
||||
try {
|
||||
final RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||
raf.seek(from);
|
||||
|
@ -58,5 +58,4 @@ public class MediaStreamer implements StreamingOutput {
|
||||
public long getLenth() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ public class WebApplicationExceptionCatcher implements ExceptionMapper<WebApplic
|
||||
}
|
||||
|
||||
private RestErrorResponse build(final WebApplicationException exception) {
|
||||
exception.printStackTrace();
|
||||
return new RestErrorResponse(exception.getResponse().getStatusInfo().toEnum(), "Catch system exception",
|
||||
exception.getMessage());
|
||||
}
|
||||
|
@ -1137,6 +1137,10 @@ public class DBAccessSQL extends DBAccess {
|
||||
}
|
||||
|
||||
public void addElement(final PreparedStatement ps, final Object value, final CountInOut iii) throws Exception {
|
||||
if (value == null) {
|
||||
ps.setNull(iii.value, Types.INTEGER);
|
||||
return;
|
||||
}
|
||||
if (value instanceof final UUID tmp) {
|
||||
final byte[] dataByte = UuidUtils.asBytes(tmp);
|
||||
ps.setBytes(iii.value, dataByte);
|
||||
|
@ -167,7 +167,7 @@ public class DataFactory {
|
||||
|
||||
final boolean createTime = elem.getDeclaredAnnotationsByType(CreationTimestamp.class).length != 0;
|
||||
final boolean updateTime = elem.getDeclaredAnnotationsByType(UpdateTimestamp.class).length != 0;
|
||||
final String comment = AnnotationTools.getComment(elem);
|
||||
final String comment = AnnotationTools.getSchemaDescription(elem);
|
||||
final String defaultValue = AnnotationTools.getDefault(elem);
|
||||
|
||||
if (mainTableBuilder.toString().length() == 0) {
|
||||
|
@ -32,7 +32,9 @@ import org.slf4j.LoggerFactory;
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.JavaType;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.type.TypeFactory;
|
||||
import com.mongodb.client.MongoCollection;
|
||||
import com.mongodb.client.model.Filters;
|
||||
|
||||
@ -162,7 +164,9 @@ public class AddOnDataJson implements DataAccessAddOn {
|
||||
}
|
||||
LOGGER.warn("Maybe fail to translate Model in datajson list: List<{}>", listClass.getCanonicalName());
|
||||
}
|
||||
final Object dataParsed = objectMapper.readValue(jsonData, field.getType());
|
||||
final TypeFactory typeFactory = objectMapper.getTypeFactory();
|
||||
final JavaType fieldType = typeFactory.constructType(field.getGenericType());
|
||||
final Object dataParsed = objectMapper.readValue(jsonData, fieldType);
|
||||
field.set(data, dataParsed);
|
||||
}
|
||||
}
|
||||
|
@ -11,13 +11,13 @@ public interface CheckFunctionInterface {
|
||||
/** This function implementation is design to check if the updated class is valid of not for insertion
|
||||
* @param baseName NAme of the object to be precise with the use of what fail.
|
||||
* @param data The object that might be injected.
|
||||
* @param filterValue List of fields that might be check. If null, then all column must be checked.
|
||||
* @param modifiedValue List of fields that might be check. If null, then all column must be checked.
|
||||
* @throws Exception Exception is generate if the data are incorrect. */
|
||||
void check(
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
Object data,
|
||||
List<String> filterValue,
|
||||
List<String> modifiedValue,
|
||||
final QueryOptions options) throws Exception;
|
||||
|
||||
default void checkAll(final DBAccess ioDb, final String baseName, final Object data, final QueryOptions options)
|
||||
|
@ -15,6 +15,7 @@ import java.util.regex.Pattern;
|
||||
import org.kar.archidata.annotation.AnnotationTools;
|
||||
import org.kar.archidata.annotation.DataJson;
|
||||
import org.kar.archidata.dataAccess.DBAccess;
|
||||
import org.kar.archidata.dataAccess.DataAccess;
|
||||
import org.kar.archidata.dataAccess.QueryCondition;
|
||||
import org.kar.archidata.dataAccess.QueryOptions;
|
||||
import org.kar.archidata.exception.DataAccessException;
|
||||
@ -35,10 +36,18 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
/** By default some element are not read like createAt and UpdatedAt. This option permit to read it. */
|
||||
public interface CheckInterface<K> {
|
||||
/** This function implementation is design to check if the updated class is valid of not for insertion
|
||||
* @param ioDb Access on the Data-Base
|
||||
* @param baseName Base of the name input that is displayed in exception generated.
|
||||
* @param data The object that might be injected.
|
||||
* @param filterValue List of fields that might be check. If null, then all column must be checked.
|
||||
* @param modifiedValue List of fields that modification is requested.
|
||||
* @param options Some query option that the checker can need to generate basic check.
|
||||
* @throws Exception Exception is generate if the data are incorrect. */
|
||||
void check(DBAccess ioDb, final String baseName, final K data, final QueryOptions options) throws Exception;
|
||||
void check(
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final K data,
|
||||
List<String> modifiedValue,
|
||||
final QueryOptions options) throws Exception;
|
||||
}
|
||||
|
||||
protected Map<String, List<CheckInterface<T>>> checking = null;
|
||||
@ -68,14 +77,24 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final String fieldName = field.getName(); // AnnotationTools.getFieldName(field);
|
||||
if (AnnotationTools.isPrimaryKey(field)) {
|
||||
add(fieldName,
|
||||
(final DBAccess ioDb, final String baseName, final T data, final QueryOptions options) -> {
|
||||
(
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
throw new InputException(baseName + fieldName,
|
||||
"This is a '@Id' (primaryKey) ==> can not be change");
|
||||
});
|
||||
}
|
||||
if (AnnotationTools.getConstraintsNotNull(field)) {
|
||||
add(fieldName,
|
||||
(final DBAccess ioDb, final String baseName, final T data, final QueryOptions options) -> {
|
||||
(
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
if (field.get(data) == null) {
|
||||
throw new InputException(baseName + fieldName, "Can not be null");
|
||||
}
|
||||
@ -83,7 +102,12 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
}
|
||||
if (AnnotationTools.isCreatedAtField(field) || AnnotationTools.isUpdateAtField(field)) {
|
||||
add(fieldName,
|
||||
(final DBAccess ioDb, final String baseName, final T data, final QueryOptions options) -> {
|
||||
(
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
throw new InputException(baseName + fieldName, "It is forbidden to change this field");
|
||||
});
|
||||
}
|
||||
@ -97,6 +121,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -116,6 +141,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -135,6 +161,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -143,7 +170,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
|
||||
final Condition conditionCheck = condCheckers.isEmpty() ? null
|
||||
: condCheckers.get(0).toCondition();
|
||||
final long count = ioDb.count(annotationManyToOne.targetEntity(), elem,
|
||||
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem,
|
||||
conditionCheck);
|
||||
if (count == 0) {
|
||||
throw new InputException(baseName + fieldName,
|
||||
@ -161,6 +188,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -181,6 +209,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -200,12 +229,13 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
return;
|
||||
}
|
||||
final long count = ioDb.count(annotationManyToOne.targetEntity(), elem);
|
||||
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
|
||||
if (count == 0) {
|
||||
throw new InputException(baseName + fieldName,
|
||||
"Foreign element does not exist in the DB:" + elem);
|
||||
@ -220,12 +250,13 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
return;
|
||||
}
|
||||
final long count = ioDb.count(annotationManyToOne.targetEntity(), elem);
|
||||
final long count = DataAccess.count(annotationManyToOne.targetEntity(), elem);
|
||||
if (count == 0) {
|
||||
throw new InputException(baseName + fieldName,
|
||||
"Foreign element does not exist in the DB:" + elem);
|
||||
@ -243,6 +274,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -263,6 +295,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -284,6 +317,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -304,6 +338,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -330,6 +365,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -349,6 +385,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -373,6 +410,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
@ -386,6 +424,28 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (AnnotationTools.getConstraintsEmail(field)) {
|
||||
final String emailPattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$";
|
||||
final Pattern pattern = Pattern.compile(emailPattern);
|
||||
add(fieldName,
|
||||
(
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final Object elem = field.get(data);
|
||||
if (elem == null) {
|
||||
return;
|
||||
}
|
||||
final String elemTyped = (String) elem;
|
||||
if (!pattern.matcher(elemTyped).find()) {
|
||||
throw new InputException(baseName + fieldName,
|
||||
"does not match the required pattern[email] (constraints) must be '"
|
||||
+ emailPattern + "'");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (type == JsonValue.class) {
|
||||
final DataJson jsonAnnotation = AnnotationTools.getDataJson(field);
|
||||
if (jsonAnnotation != null && jsonAnnotation.checker() != CheckFunctionVoid.class) {
|
||||
@ -397,6 +457,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
instance.checkAll(ioDb, baseName + fieldName + ".", field.get(data), options);
|
||||
});
|
||||
@ -408,14 +469,19 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
if (AnnotationTools.isUnique(field)) {
|
||||
// Create the request ...
|
||||
add(fieldName,
|
||||
(final DBAccess ioDb, final String baseName, final T data, final QueryOptions options) -> {
|
||||
(
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final T data,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) -> {
|
||||
final List<ConditionChecker> condCheckers = options.get(ConditionChecker.class);
|
||||
Object other = null;
|
||||
if (condCheckers.isEmpty()) {
|
||||
other = ioDb.getWhere(this.clazz,
|
||||
other = DataAccess.getWhere(this.clazz,
|
||||
new Condition(new QueryCondition(fieldName, "==", field.get(data))));
|
||||
} else {
|
||||
other = ioDb.getWhere(this.clazz,
|
||||
other = DataAccess.getWhere(this.clazz,
|
||||
new Condition(new QueryCondition(fieldName, "==", field.get(data))),
|
||||
condCheckers.get(0).toCondition());
|
||||
}
|
||||
@ -446,7 +512,7 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
final DBAccess ioDb,
|
||||
final String baseName,
|
||||
final Object data,
|
||||
final List<String> filterValue,
|
||||
final List<String> modifiedValue,
|
||||
final QueryOptions options) throws Exception {
|
||||
if (this.checking == null) {
|
||||
initialize();
|
||||
@ -456,19 +522,21 @@ public class CheckJPA<T> implements CheckFunctionInterface {
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
final T dataCasted = (T) data;
|
||||
for (final String filter : filterValue) {
|
||||
for (final String filter : modifiedValue) {
|
||||
final List<CheckInterface<T>> actions = this.checking.get(filter);
|
||||
if (actions == null) {
|
||||
continue;
|
||||
}
|
||||
for (final CheckInterface<T> action : actions) {
|
||||
action.check(ioDb, baseName, dataCasted, options);
|
||||
action.check(ioDb, baseName, dataCasted, modifiedValue, options);
|
||||
}
|
||||
}
|
||||
checkTyped(dataCasted, filterValue, options);
|
||||
checkTyped(dataCasted, modifiedValue, options);
|
||||
}
|
||||
|
||||
public void checkTyped(final T data, final List<String> filterValue, final QueryOptions options) throws Exception {
|
||||
public void checkTyped(final T data, final List<String> modifiedValue, final QueryOptions options)
|
||||
throws Exception {
|
||||
// nothing to do ...
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,46 +0,0 @@
|
||||
package org.kar.archidata.dataAccess.options;
|
||||
|
||||
@Deprecated
|
||||
public class DBInterfaceOption extends QueryOption {
|
||||
/*
|
||||
private DBEntry entry = null;
|
||||
private final DBConfig config;
|
||||
private final boolean root;
|
||||
|
||||
public DBInterfaceOption(final DBConfig config) {
|
||||
this.config = config;
|
||||
this.root = false;
|
||||
}
|
||||
|
||||
public DBInterfaceOption(final DBConfig config, final boolean root) {
|
||||
this.config = config;
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
public DBEntry getEntry(final QueryOptions options) throws IOException {
|
||||
if (this.entry == null) {
|
||||
final List<DBInterfaceRoot> isRoot = options.get(DBInterfaceRoot.class);
|
||||
this.entry = DBEntry.createInterface(this.config, isRoot.size() == 1 && isRoot.get(0).getRoot());
|
||||
}
|
||||
return this.entry;
|
||||
}
|
||||
|
||||
public boolean getRoot() {
|
||||
return this.root;
|
||||
}
|
||||
|
||||
public static DBEntry getAutoEntry(final QueryOptions options) throws IOException {
|
||||
if (options == null) {
|
||||
return DBEntry.createInterface(GlobalConfiguration.dbConfig, false);
|
||||
}
|
||||
final List<DBInterfaceOption> dbOption = options.get(DBInterfaceOption.class);
|
||||
if (dbOption.size() == 0) {
|
||||
final List<DBInterfaceRoot> isRoot = options.get(DBInterfaceRoot.class);
|
||||
return DBEntry.createInterface(GlobalConfiguration.dbConfig, isRoot.size() == 1 && isRoot.get(0).getRoot());
|
||||
} else {
|
||||
return dbOption.get(0).getEntry(options);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
@ -136,7 +136,7 @@ public class ClassObjectModel extends ClassModel {
|
||||
this(field.getName(), //
|
||||
ClassModel.getModel(field.getGenericType(), previous), //
|
||||
getSubModelIfExist(field, previous), //
|
||||
AnnotationTools.getComment(field), //
|
||||
AnnotationTools.getSchemaDescription(field), //
|
||||
getStringMinSize(field), //
|
||||
getStringMaxSize(field), //
|
||||
AnnotationTools.getConstraintsMin(field), //
|
||||
|
@ -12,6 +12,7 @@ import java.util.Map.Entry;
|
||||
|
||||
import org.kar.archidata.annotation.security.PermitTokenInURI;
|
||||
import org.kar.archidata.catcher.RestErrorResponse;
|
||||
import org.kar.archidata.exception.SystemException;
|
||||
import org.kar.archidata.model.UserByToken;
|
||||
import org.kar.archidata.tools.JWTWrapper;
|
||||
import org.slf4j.Logger;
|
||||
@ -23,6 +24,7 @@ import jakarta.annotation.Priority;
|
||||
import jakarta.annotation.security.DenyAll;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Priorities;
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
import jakarta.ws.rs.container.ContainerRequestFilter;
|
||||
@ -42,18 +44,40 @@ public class AuthenticationFilter implements ContainerRequestFilter {
|
||||
@Context
|
||||
private ResourceInfo resourceInfo;
|
||||
protected final String applicationName;
|
||||
protected final String issuer;
|
||||
|
||||
public static final String AUTHENTICATION_SCHEME = "Bearer";
|
||||
public static final String APIKEY = "ApiKey";
|
||||
|
||||
public AuthenticationFilter(final String applicationName) {
|
||||
this.applicationName = applicationName;
|
||||
this.issuer = "KarAuth";
|
||||
}
|
||||
|
||||
public AuthenticationFilter(final String applicationName, final String issuer) {
|
||||
this.applicationName = applicationName;
|
||||
this.issuer = issuer;
|
||||
}
|
||||
|
||||
public String getRequestedPath(final ContainerRequestContext requestContext) {
|
||||
final Class<?> resourceClass = this.resourceInfo.getResourceClass();
|
||||
final Method resourceMethod = this.resourceInfo.getResourceMethod();
|
||||
final String classPath = resourceClass.isAnnotationPresent(Path.class)
|
||||
? resourceClass.getAnnotation(Path.class).value()
|
||||
: "";
|
||||
final String methodPath = resourceMethod.isAnnotationPresent(Path.class)
|
||||
? resourceMethod.getAnnotation(Path.class).value()
|
||||
: "";
|
||||
final String fullPath = (classPath.startsWith("/") ? "" : "/") + classPath
|
||||
+ (methodPath.startsWith("/") ? "" : "/") + methodPath;
|
||||
return fullPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void filter(final ContainerRequestContext requestContext) throws IOException {
|
||||
/* logger.debug("-----------------------------------------------------"); logger.debug("---- Check if have authorization ----");
|
||||
* logger.debug("-----------------------------------------------------"); logger.debug(" for:{}", requestContext.getUriInfo().getPath()); */
|
||||
|
||||
final Method method = this.resourceInfo.getResourceMethod();
|
||||
// Access denied for all
|
||||
if (method.isAnnotationPresent(DenyAll.class)) {
|
||||
@ -140,12 +164,13 @@ public class AuthenticationFilter implements ContainerRequestFilter {
|
||||
final List<String> roles = Arrays.asList(rolesAnnotation.value());
|
||||
// check if the user have the right:
|
||||
boolean haveRight = false;
|
||||
for (final String role : roles) {
|
||||
if (userContext.isUserInRole(role)) {
|
||||
haveRight = true;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
haveRight = checkRight(requestContext, userContext, roles);
|
||||
} catch (final SystemException e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Is user valid?
|
||||
if (!haveRight) {
|
||||
LOGGER.error("REJECTED not enought right : {} require: {}", requestContext.getUriInfo().getPath(), roles);
|
||||
@ -157,6 +182,18 @@ public class AuthenticationFilter implements ContainerRequestFilter {
|
||||
// logger.debug("Get local user : {} / {}", user, userByToken);
|
||||
}
|
||||
|
||||
protected boolean checkRight(
|
||||
final ContainerRequestContext requestContext,
|
||||
final MySecurityContext userContext,
|
||||
final List<String> roles) throws SystemException {
|
||||
for (final String role : roles) {
|
||||
if (userContext.isUserInRole(this.applicationName + "/" + role)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isTokenBasedAuthentication(final String authorizationHeader) {
|
||||
// Check if the Authorization header is valid
|
||||
// It must not be null and must be prefixed with "Bearer" plus a whitespace
|
||||
@ -193,7 +230,7 @@ public class AuthenticationFilter implements ContainerRequestFilter {
|
||||
// must be override to be good implementation
|
||||
protected UserByToken validateJwtToken(final String authorization) throws Exception {
|
||||
// logger.debug(" validate token : " + authorization);
|
||||
final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, "KarAuth", null);
|
||||
final JWTClaimsSet ret = JWTWrapper.validateToken(authorization, this.issuer, null);
|
||||
// check the token is valid !!! (signed and coherent issuer...
|
||||
if (ret == null) {
|
||||
LOGGER.error("The token is not valid: '{}'", authorization);
|
||||
@ -208,13 +245,16 @@ public class AuthenticationFilter implements ContainerRequestFilter {
|
||||
user.type = UserByToken.TYPE_USER;
|
||||
final Object rowRight = ret.getClaim("right");
|
||||
if (rowRight != null) {
|
||||
final Map<String, Map<String, Object>> rights = (Map<String, Map<String, Object>>) ret.getClaim("right");
|
||||
LOGGER.info("Detect right in Authentication Filter: {}", rowRight);
|
||||
user.right = (Map<String, Map<String, Object>>) ret.getClaim("right");
|
||||
/*
|
||||
if (rights.containsKey(this.applicationName)) {
|
||||
user.right = rights.get(this.applicationName);
|
||||
} else {
|
||||
LOGGER.error("Connect with no right for this application='{}' full Right='{}'", this.applicationName,
|
||||
rights);
|
||||
}
|
||||
*/
|
||||
}
|
||||
// logger.debug("request user: '{}' right: '{}' row='{}'", userUID, user.right, rowRight);
|
||||
return user;
|
||||
|
@ -16,9 +16,12 @@ public class CORSFilter implements ContainerResponseFilter {
|
||||
// System.err.println("filter cors ..." + request.toString());
|
||||
|
||||
response.getHeaders().add("Access-Control-Allow-Origin", "*");
|
||||
response.getHeaders().add("Access-Control-Allow-Range", "bytes");
|
||||
response.getHeaders().add("access-control-expose-headers", "range");
|
||||
response.getHeaders().add("Access-Control-Allow-Headers",
|
||||
"Origin, content-type, Content-type, Accept, Authorization, mime-type, filename");
|
||||
"Origin, content-type, Content-type, Accept, Authorization, mime-type, filename, Range");
|
||||
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
|
||||
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD");
|
||||
response.getHeaders().add("Access-Control-Allow-Methods",
|
||||
"GET, POST, PUT, PATCH, DELETE, ARCHIVE, RESTORE, OPTIONS, HEAD");
|
||||
}
|
||||
}
|
||||
|
@ -19,4 +19,11 @@ public class GenericContext implements Principal {
|
||||
}
|
||||
return this.userByToken.name;
|
||||
}
|
||||
|
||||
public Long getUserID() {
|
||||
if (this.userByToken != null) {
|
||||
return this.userByToken.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
package org.kar.archidata.filter;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Set;
|
||||
|
||||
import org.kar.archidata.model.UserByToken;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jakarta.ws.rs.core.SecurityContext;
|
||||
|
||||
// https://simplapi.wordpress.com/2015/09/19/jersey-jax-rs-securitycontext-in-action/
|
||||
class MySecurityContext implements SecurityContext {
|
||||
public class MySecurityContext implements SecurityContext {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(MySecurityContext.class);
|
||||
|
||||
private final GenericContext contextPrincipale;
|
||||
private final String sheme;
|
||||
@ -22,17 +26,128 @@ class MySecurityContext implements SecurityContext {
|
||||
return this.contextPrincipale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(final String role) {
|
||||
public Object getRightOfRoleInGroup(final String group, final String role) {
|
||||
if (this.contextPrincipale.userByToken != null) {
|
||||
final Object value = this.contextPrincipale.userByToken.right.get(role);
|
||||
if (value instanceof final Boolean ret) {
|
||||
return ret;
|
||||
}
|
||||
return this.contextPrincipale.userByToken.getRight(group, role);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Set<String> getGroups() {
|
||||
if (this.contextPrincipale.userByToken != null) {
|
||||
return this.contextPrincipale.userByToken.getGroups();
|
||||
}
|
||||
return Set.of();
|
||||
}
|
||||
|
||||
public boolean groupExist(final String group) {
|
||||
if (this.contextPrincipale.userByToken != null) {
|
||||
return this.contextPrincipale.userByToken.groupExist(group);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Not sure the Long type is definitive.
|
||||
public Long getUserID() {
|
||||
if (this.contextPrincipale.userByToken != null) {
|
||||
return this.contextPrincipale.userByToken.id;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean checkRightInGroup(
|
||||
final String group,
|
||||
final String role,
|
||||
final boolean needRead,
|
||||
final boolean needWrite) {
|
||||
if ("USER".equals(role)) {
|
||||
if (groupExist(group)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// get associated Roles:
|
||||
final Object rightPart = getRightOfRoleInGroup(group, role);
|
||||
LOGGER.info("detect : {}", rightPart);
|
||||
long dataRight = 0;
|
||||
if (rightPart instanceof final Long rightPartCasted) {
|
||||
dataRight = rightPartCasted;
|
||||
} else if (rightPart instanceof final Integer rightPartCasted) {
|
||||
dataRight = rightPartCasted;
|
||||
}
|
||||
if (dataRight == PartRight.READ_WRITE.getValue()) {
|
||||
return true;
|
||||
}
|
||||
if (!needRead && needWrite && dataRight == PartRight.WRITE.getValue()) {
|
||||
return true;
|
||||
}
|
||||
if (needRead && !needWrite && dataRight == PartRight.READ.getValue()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(final String role) {
|
||||
String roleEdit = role;
|
||||
boolean needRead = false;
|
||||
boolean needWrite = false;
|
||||
// Check if the API overwrite the right needed for this API.
|
||||
if (roleEdit.contains(":")) {
|
||||
if (roleEdit.endsWith(":w")) {
|
||||
try {
|
||||
roleEdit = roleEdit.substring(0, roleEdit.length() - 2);
|
||||
} catch (final IndexOutOfBoundsException ex) {
|
||||
LOGGER.error("Fail to extract role of '{}'", role);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
needWrite = true;
|
||||
} else if (roleEdit.endsWith(":r")) {
|
||||
try {
|
||||
roleEdit = roleEdit.substring(0, roleEdit.length() - 2);
|
||||
} catch (final IndexOutOfBoundsException ex) {
|
||||
LOGGER.error("Fail to extract role of '{}'", role);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
needRead = true;
|
||||
} else if (roleEdit.endsWith(":rw")) {
|
||||
try {
|
||||
roleEdit = roleEdit.substring(0, roleEdit.length() - 3);
|
||||
} catch (final IndexOutOfBoundsException ex) {
|
||||
LOGGER.error("Fail to extract role of '{}'", role);
|
||||
ex.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
needRead = true;
|
||||
needWrite = true;
|
||||
} else {
|
||||
LOGGER.error("Request check right of an unknow right mode: {} (after ':')", roleEdit);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (roleEdit.contains("/")) {
|
||||
final String[] elements = roleEdit.split("/");
|
||||
return checkRightInGroup(elements[0], elements[1], needRead, needWrite);
|
||||
}
|
||||
// Special case, if the token is valid, it is an USER ...
|
||||
if ("USER".equals(roleEdit)) {
|
||||
return true;
|
||||
}
|
||||
return checkRightInGroup("?system?", roleEdit, needRead, needWrite);
|
||||
}
|
||||
|
||||
public Object getRole(final String role) {
|
||||
LOGGER.info("contextPrincipale={}", this.contextPrincipale);
|
||||
if (this.contextPrincipale.userByToken != null) {
|
||||
LOGGER.info("contextPrincipale.userByToken={}", this.contextPrincipale.userByToken);
|
||||
LOGGER.info("contextPrincipale.userByToken.right={}", this.contextPrincipale.userByToken.right);
|
||||
return this.contextPrincipale.userByToken.right.get(role);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
return "https".equalsIgnoreCase(this.sheme);
|
||||
|
30
src/org/kar/archidata/filter/PartRight.java
Normal file
30
src/org/kar/archidata/filter/PartRight.java
Normal file
@ -0,0 +1,30 @@
|
||||
package org.kar.archidata.filter;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonValue;
|
||||
|
||||
public enum PartRight {
|
||||
NONE(0), //
|
||||
READ(1), //
|
||||
WRITE(2), //
|
||||
READ_WRITE(3);
|
||||
|
||||
private final int value;
|
||||
|
||||
PartRight(final int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@JsonValue
|
||||
public int getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public static PartRight fromValue(final int value) {
|
||||
for (final PartRight element : values()) {
|
||||
if (element.getValue() == value) {
|
||||
return element;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("PartRight: Unknown value: " + value);
|
||||
}
|
||||
}
|
@ -10,7 +10,6 @@ import jakarta.ws.rs.DefaultValue;
|
||||
|
||||
public class UUIDGenericData extends GenericTiming {
|
||||
@Id
|
||||
|
||||
@DefaultValue("(UUID_TO_BIN(UUID(), TRUE))")
|
||||
@Column(nullable = false, unique = true)
|
||||
@Schema(description = "Unique UUID of the object", required = false, readOnly = true, example = "e6b33c1c-d24d-11ee-b616-02420a030102")
|
||||
|
@ -28,26 +28,29 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Pattern;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
|
||||
@Table(name = "user")
|
||||
@DataIfNotExists
|
||||
@JsonInclude(JsonInclude.Include.NON_NULL)
|
||||
public class User extends GenericDataSoftDelete {
|
||||
@NotNull
|
||||
@Column(length = 128)
|
||||
@Size(min = 3, max = 128)
|
||||
@Pattern(regexp = "^[a-zA-Z0-9-_ \\.]+$")
|
||||
public String login = null;
|
||||
|
||||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
|
||||
public Timestamp lastConnection = null;
|
||||
@DefaultValue("'0'")
|
||||
@Column(nullable = false)
|
||||
public boolean admin = false;
|
||||
|
||||
@DefaultValue("'0'")
|
||||
@Column(nullable = false)
|
||||
public boolean blocked = false;
|
||||
@DefaultValue("'0'")
|
||||
@Column(nullable = false)
|
||||
public boolean removed = false;
|
||||
@Column(length = 512)
|
||||
public String blockedReason;
|
||||
|
||||
@Schema(description = "List of Id of the specific covers")
|
||||
@DataJson(targetEntity = Data.class)
|
||||
@ -56,7 +59,8 @@ public class User extends GenericDataSoftDelete {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "User [login=" + this.login + ", last=" + this.lastConnection + ", admin=" + this.admin + "]";
|
||||
return "User [login=" + this.login + ", last=" + this.lastConnection + ", blocked=" + this.blocked
|
||||
+ ", blockedReason=" + this.blockedReason + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package org.kar.archidata.model;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class UserByToken {
|
||||
public static final int TYPE_USER = -1;
|
||||
@ -13,13 +14,35 @@ public class UserByToken {
|
||||
public Long parentId = null; // FOr application, this is the id of the application, and of user token, this is the USERID
|
||||
public String name = null;
|
||||
// Right map
|
||||
public Map<String, Object> right = new HashMap<>();
|
||||
public Map<String, Map<String, Object>> right = new HashMap<>();
|
||||
|
||||
public boolean hasRight(final String key, final Object value) {
|
||||
if (!this.right.containsKey(key)) {
|
||||
public Set<String> getGroups() {
|
||||
return this.right.keySet();
|
||||
}
|
||||
|
||||
public boolean groupExist(final String group) {
|
||||
if (!this.right.containsKey(group)) {
|
||||
return false;
|
||||
}
|
||||
return this.right.containsKey(group);
|
||||
}
|
||||
|
||||
public Object getRight(final String group, final String key) {
|
||||
if (!this.right.containsKey(group)) {
|
||||
return null;
|
||||
}
|
||||
final Map<String, Object> rightGroup = this.right.get(group);
|
||||
if (!rightGroup.containsKey(key)) {
|
||||
return null;
|
||||
}
|
||||
return rightGroup.get(key);
|
||||
}
|
||||
|
||||
public boolean hasRight(final String group, final String key, final Object value) {
|
||||
final Object data = getRight(group, key);
|
||||
if (data == null) {
|
||||
return false;
|
||||
}
|
||||
final Object data = this.right.get(key);
|
||||
if (data instanceof final Boolean elem) {
|
||||
if (value instanceof final Boolean castVal) {
|
||||
if (elem.equals(castVal)) {
|
||||
|
@ -121,7 +121,7 @@ public class RESTApi {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body)
|
||||
public <T, U> T modelSendJson(final String model, final Class<T> clazz, final String urlOffset, String body)
|
||||
throws RESTErrorResponseExeption, IOException, InterruptedException {
|
||||
final HttpClient client = HttpClient.newHttpClient();
|
||||
// client.property(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND, true);
|
||||
@ -166,7 +166,7 @@ public class RESTApi {
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected <T> T modelSendMap(
|
||||
public <T> T modelSendMap(
|
||||
final String model,
|
||||
final Class<T> clazz,
|
||||
final String urlOffset,
|
||||
|
@ -7,11 +7,15 @@
|
||||
import { RestErrorResponse, isRestErrorResponse } from "./model";
|
||||
|
||||
export enum HTTPRequestModel {
|
||||
ARCHIVE = "ARCHIVE",
|
||||
DELETE = "DELETE",
|
||||
HEAD = "HEAD",
|
||||
GET = "GET",
|
||||
OPTION = "OPTION",
|
||||
PATCH = "PATCH",
|
||||
POST = "POST",
|
||||
PUT = "PUT",
|
||||
RESTORE = "RESTORE",
|
||||
}
|
||||
export enum HTTPMimeType {
|
||||
ALL = "*/*",
|
||||
@ -248,9 +252,14 @@ export function RESTRequest({
|
||||
if (restModel.accept !== undefined) {
|
||||
headers["Accept"] = restModel.accept;
|
||||
}
|
||||
if (restModel.requestType !== HTTPRequestModel.GET) {
|
||||
if (restModel.requestType !== HTTPRequestModel.GET &&
|
||||
restModel.requestType !== HTTPRequestModel.ARCHIVE &&
|
||||
restModel.requestType !== HTTPRequestModel.RESTORE
|
||||
) {
|
||||
// if Get we have not a content type, the body is empty
|
||||
if (restModel.contentType !== HTTPMimeType.MULTIPART) {
|
||||
if (restModel.contentType !== HTTPMimeType.MULTIPART &&
|
||||
restModel.contentType !== undefined
|
||||
) {
|
||||
// special case of multi-part ==> no content type otherwise the browser does not set the ";bundary=--****"
|
||||
headers["Content-Type"] = restModel.contentType;
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ import test.kar.archidata.dataAccess.model.TypesTable;
|
||||
|
||||
public class ConfigureDb {
|
||||
final static private Logger LOGGER = LoggerFactory.getLogger(ConfigureDb.class);
|
||||
final static private String modeTestForced = null; // "MONGO";
|
||||
final static private String modeTestForced = null;// "MONGO";
|
||||
public static DBAccess da = null;
|
||||
|
||||
public static void configure() throws IOException, InternalServerErrorException, DataAccessException {
|
||||
|
@ -1 +1 @@
|
||||
0.14.3-dev
|
||||
0.19.1-dev
|
||||
|
Loading…
x
Reference in New Issue
Block a user