diff --git a/http-client/mega.postman_collection.json b/http-client/mega.postman_collection.json index 32cffe3cb..9f2507526 100644 --- a/http-client/mega.postman_collection.json +++ b/http-client/mega.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "120b4182-530c-427c-80ff-507add82a277", + "_postman_id": "40539f4c-39cb-4b6d-80fc-df63c0882d12", "name": "mega", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "7412098" @@ -140,7 +140,122 @@ }, "response": [] } + ], + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "clientSecret", + "value": "{{clientSecret}}", + "type": "string" + }, + { + "key": "client_authentication", + "value": "header", + "type": "string" + }, + { + "key": "scope", + "value": "", + "type": "string" + }, + { + "key": "clientId", + "value": "mega-cron", + "type": "string" + }, + { + "key": "accessTokenUrl", + "value": "https://gepardec-sso-qa.apps.cloudscale-lpg-2.appuio.cloud/realms/gepardec/protocol/openid-connect/token", + "type": "string" + }, + { + "key": "grant_type", + "value": "client_credentials", + "type": "string" + }, + { + "key": "tokenName", + "value": "mega-cron", + "type": "string" + }, + { + "key": "refreshRequestParams", + "value": [], + "type": "any" + }, + { + "key": "tokenRequestParams", + "value": [], + "type": "any" + }, + { + "key": "authRequestParams", + "value": [], + "type": "any" + }, + { + "key": "challengeAlgorithm", + "value": "S256", + "type": "string" + }, + { + "key": "useBrowser", + "value": true, + "type": "boolean" + }, + { + "key": "authUrl", + "value": "https://accounts.google.com/o/oauth2/v2/auth", + "type": "string" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } ] } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } ] -} \ No newline at end of file +} diff --git a/pom.xml b/pom.xml index 25991a4bd..cce95f456 100644 --- a/pom.xml +++ b/pom.xml @@ -55,6 +55,13 @@ pom import + + com.google.api-client + google-api-client-bom + 2.3.0 + pom + import + @@ -96,9 +103,23 @@ io.quarkus quarkus-smallrye-metrics + + com.google.api-client + google-api-client + + + com.google.apis + google-api-services-gmail + v1-rev20231218-2.0.0 + + + com.google.auth + google-auth-library-oauth2-http + 1.23.0 + io.quarkus - quarkus-smallrye-jwt + quarkus-oidc @@ -117,7 +138,7 @@ org.postgresql postgresql - 42.6.1 + 42.7.2 io.quarkus @@ -213,7 +234,7 @@ io.quarkus - quarkus-test-security-jwt + quarkus-test-security-oidc test diff --git a/src/main/java/com/gepardec/mega/application/configuration/GoogleCloudConfig.java b/src/main/java/com/gepardec/mega/application/configuration/GoogleCloudConfig.java new file mode 100644 index 000000000..3b5676063 --- /dev/null +++ b/src/main/java/com/gepardec/mega/application/configuration/GoogleCloudConfig.java @@ -0,0 +1,51 @@ +package com.gepardec.mega.application.configuration; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import org.eclipse.microprofile.config.inject.ConfigProperty; + +import java.util.List; + +@ApplicationScoped +public class GoogleCloudConfig { + + @Inject + @ConfigProperty(name = "mega.google.service-account-key") + String serviceAccountKey; + + @Inject + @ConfigProperty(name = "mega.google.gmail.api.application-name") + String applicationName; + + @Inject + @ConfigProperty(name = "mega.google.gmail.api.workspace-user") + String workspaceUser; + + @Inject + @ConfigProperty(name = "mega.google.gmail.api.label-filter") + List labelFilter; + + @Inject + @ConfigProperty(name = "mega.google.pubsub.topic") + String topicName; + + public String getServiceAccountKey() { + return serviceAccountKey; + } + + public String getApplicationName() { + return applicationName; + } + + public String getWorkspaceUser() { + return workspaceUser; + } + + public List getLabelFilter() { + return labelFilter; + } + + public String getTopicName() { + return topicName; + } +} diff --git a/src/main/java/com/gepardec/mega/application/filter/MegaCronSecuritySchemaOASFilter.java b/src/main/java/com/gepardec/mega/application/filter/MegaCronSecuritySchemaOASFilter.java new file mode 100644 index 000000000..1f5ac3efe --- /dev/null +++ b/src/main/java/com/gepardec/mega/application/filter/MegaCronSecuritySchemaOASFilter.java @@ -0,0 +1,18 @@ +package com.gepardec.mega.application.filter; + +import org.eclipse.microprofile.config.ConfigProvider; +import org.eclipse.microprofile.openapi.OASFilter; +import org.eclipse.microprofile.openapi.models.security.SecurityScheme; + +public class MegaCronSecuritySchemaOASFilter implements OASFilter { + + @Override + public SecurityScheme filterSecurityScheme(SecurityScheme securityScheme) { + if (securityScheme.getType() == SecurityScheme.Type.OAUTH2) { + securityScheme.getFlows().getClientCredentials().tokenUrl(ConfigProvider.getConfig().getConfigValue("mega.oauth.issuer").getValue() + "/protocol/openid-connect/token"); + return OASFilter.super.filterSecurityScheme(securityScheme); + } else { + return null; + } + } +} diff --git a/src/main/java/com/gepardec/mega/application/interceptor/RolesAllowed.java b/src/main/java/com/gepardec/mega/application/interceptor/MegaRolesAllowed.java similarity index 93% rename from src/main/java/com/gepardec/mega/application/interceptor/RolesAllowed.java rename to src/main/java/com/gepardec/mega/application/interceptor/MegaRolesAllowed.java index cf16cd045..52cee923b 100644 --- a/src/main/java/com/gepardec/mega/application/interceptor/RolesAllowed.java +++ b/src/main/java/com/gepardec/mega/application/interceptor/MegaRolesAllowed.java @@ -14,7 +14,7 @@ @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.METHOD}) -public @interface RolesAllowed { +public @interface MegaRolesAllowed { @Nonbinding Role[] value() default {Role.EMPLOYEE}; diff --git a/src/main/java/com/gepardec/mega/application/interceptor/RolesAllowedInterceptor.java b/src/main/java/com/gepardec/mega/application/interceptor/MegaRolesAllowedInterceptor.java similarity index 74% rename from src/main/java/com/gepardec/mega/application/interceptor/RolesAllowedInterceptor.java rename to src/main/java/com/gepardec/mega/application/interceptor/MegaRolesAllowedInterceptor.java index fee23f8f9..702d46d32 100644 --- a/src/main/java/com/gepardec/mega/application/interceptor/RolesAllowedInterceptor.java +++ b/src/main/java/com/gepardec/mega/application/interceptor/MegaRolesAllowedInterceptor.java @@ -13,24 +13,24 @@ import java.util.Set; @Interceptor -@RolesAllowed +@MegaRolesAllowed @Priority(Interceptor.Priority.APPLICATION) -public class RolesAllowedInterceptor { +public class MegaRolesAllowedInterceptor { @Inject UserContext userContext; @AroundInvoke public Object intercept(InvocationContext invocationContext) throws Exception { - RolesAllowed rolesAllowedAnnotation = invocationContext.getMethod().getAnnotation(RolesAllowed.class); - if (rolesAllowedAnnotation == null) { - rolesAllowedAnnotation = invocationContext.getTarget().getClass().getAnnotation(RolesAllowed.class); + MegaRolesAllowed megaRolesAllowedAnnotation = invocationContext.getMethod().getAnnotation(MegaRolesAllowed.class); + if (megaRolesAllowedAnnotation == null) { + megaRolesAllowedAnnotation = invocationContext.getTarget().getClass().getAnnotation(MegaRolesAllowed.class); } - Objects.requireNonNull(rolesAllowedAnnotation, + Objects.requireNonNull(megaRolesAllowedAnnotation, "Could not resolve Authorizaion annotation. Do you use Stereotype annotations, which are currently not supported?"); - Role[] allowedRoles = rolesAllowedAnnotation.value(); + Role[] allowedRoles = megaRolesAllowedAnnotation.value(); if (isInRole(userContext.getUser().getRoles(), allowedRoles)) { return invocationContext.proceed(); } else { diff --git a/src/main/java/com/gepardec/mega/application/observer/LifecycleObserver.java b/src/main/java/com/gepardec/mega/application/observer/LifecycleObserver.java index e3265d99f..a21cf9808 100644 --- a/src/main/java/com/gepardec/mega/application/observer/LifecycleObserver.java +++ b/src/main/java/com/gepardec/mega/application/observer/LifecycleObserver.java @@ -1,7 +1,10 @@ package com.gepardec.mega.application.observer; import com.gepardec.mega.application.configuration.ApplicationConfig; +import com.gepardec.mega.service.api.GmailService; +import io.quarkus.arc.profile.IfBuildProfile; import io.quarkus.runtime.StartupEvent; +import io.quarkus.runtime.configuration.ConfigUtils; import jakarta.enterprise.context.Dependent; import jakarta.enterprise.event.Observes; import liquibase.Contexts; @@ -38,4 +41,13 @@ void initDatabase(final @Observes StartupEvent event, logger.error("Initialization of the database with liquibase failed", e); } } + + void watchGmailInbox(final @Observes StartupEvent event, + final GmailService gmailService, + final Logger logger) { + if (ConfigUtils.isProfileActive("prod")) { + logger.info("Starting to watch the Gmail inbox. Renewals will be handled by the corresponding scheduled task."); + gmailService.watchInbox(); + } + } } diff --git a/src/main/java/com/gepardec/mega/application/producer/GoogleCredentialsProducer.java b/src/main/java/com/gepardec/mega/application/producer/GoogleCredentialsProducer.java new file mode 100644 index 000000000..35827517f --- /dev/null +++ b/src/main/java/com/gepardec/mega/application/producer/GoogleCredentialsProducer.java @@ -0,0 +1,27 @@ +package com.gepardec.mega.application.producer; + +import com.gepardec.mega.application.configuration.GoogleCloudConfig; +import com.google.api.services.gmail.GmailScopes; +import com.google.auth.oauth2.GoogleCredentials; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.Collections; + +@ApplicationScoped +public class GoogleCredentialsProducer { + + @Inject + GoogleCloudConfig googleCloudConfig; + + @Produces + @ApplicationScoped + public GoogleCredentials produceGoogleCredentials() throws IOException { + return GoogleCredentials.fromStream(new ByteArrayInputStream(googleCloudConfig.getServiceAccountKey().getBytes())) + .createScoped(Collections.singleton(GmailScopes.GMAIL_READONLY)) + .createDelegated(googleCloudConfig.getWorkspaceUser()); + } +} diff --git a/src/main/java/com/gepardec/mega/application/schedule/MailSchedules.java b/src/main/java/com/gepardec/mega/application/schedule/MailSchedules.java new file mode 100644 index 000000000..ce85e27c6 --- /dev/null +++ b/src/main/java/com/gepardec/mega/application/schedule/MailSchedules.java @@ -0,0 +1,33 @@ +package com.gepardec.mega.application.schedule; + +import com.gepardec.mega.notification.mail.ReminderEmailSender; +import com.gepardec.mega.service.api.GmailService; +import io.quarkus.scheduler.Scheduled; +import jakarta.enterprise.context.Dependent; +import jakarta.inject.Inject; + +@Dependent +public class MailSchedules { + + @Inject + ReminderEmailSender reminderEmailSender; + + @Inject + GmailService gmailService; + + @Scheduled( + identity = "Send E-Mail reminder to users", + cron = "0 0 7 ? * MON-FRI" + ) + void sendReminder() { + reminderEmailSender.sendReminder(); + } + + @Scheduled( + identity = "Renew the mailbox watch for the Gmail API every day at 06:00", + cron = "0 0 6 * * ? *" + ) + void renewMailboxWatch() { + gmailService.watchInbox(); + } +} diff --git a/src/main/java/com/gepardec/mega/application/schedule/Schedules.java b/src/main/java/com/gepardec/mega/application/schedule/Schedules.java deleted file mode 100644 index 834a49ccd..000000000 --- a/src/main/java/com/gepardec/mega/application/schedule/Schedules.java +++ /dev/null @@ -1,102 +0,0 @@ -package com.gepardec.mega.application.schedule; - -import com.gepardec.mega.domain.utils.DateUtils; -import com.gepardec.mega.notification.mail.ReminderEmailSender; -import com.gepardec.mega.notification.mail.receiver.MailReceiver; -import com.gepardec.mega.rest.api.SyncResource; -import com.gepardec.mega.service.api.EnterpriseSyncService; -import com.gepardec.mega.service.api.PrematureEmployeeCheckSyncService; -import com.gepardec.mega.service.api.ProjectSyncService; -import com.gepardec.mega.service.api.StepEntrySyncService; -import com.gepardec.mega.service.api.SyncService; -import io.quarkus.scheduler.Scheduled; -import jakarta.enterprise.context.Dependent; -import jakarta.inject.Inject; - -import java.time.LocalDate; -import java.util.concurrent.TimeUnit; - -/** - * @author Thomas Herzog - * @since 10/3/2020 - */ -@Dependent -public class Schedules { - - // Documentation: https://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm - - @Inject - SyncService syncService; - - @Inject - StepEntrySyncService stepEntrySyncService; - - - @Inject - ProjectSyncService projectSyncService; - - @Inject - EnterpriseSyncService enterpriseSyncService; - - @Inject - PrematureEmployeeCheckSyncService prematureEmployeeCheckSyncService; - - @Inject - ReminderEmailSender reminderEmailSender; - - @Inject - MailReceiver mailReceiver; - - @Scheduled(identity = "Sync ZEP-Employees with Users in the database every 30 minutes", - every = "PT30M", - delay = 15, delayUnit = TimeUnit.SECONDS) - // We need to wait for liquibase to finish, but is executed in parallel - void syncEmployeesWithDatabase() { - syncService.syncEmployees(); - } - - @Scheduled(identity = "Generate step entries on the last day of a month at 00:00", - cron = "0 0 0 L * ? *") - void generateStepEntriesDefault() { - stepEntrySyncService.generateStepEntriesFromScheduler(); - } - - @Scheduled(identity = "Update project entries every 30 minutes", - every = "PT30M", - delay = 30, delayUnit = TimeUnit.SECONDS) - void generateProjects() { - projectSyncService.generateProjects(); - } - - @Scheduled(identity = "Generate enterprise entries on the last day of a month at 00:00", - cron = "0 0 0 L * ? *") - void generateEnterpriseEntries() { - enterpriseSyncService.generateEnterpriseEntries(LocalDate.now().withDayOfMonth(1)); - } - - @Scheduled(identity = "Send E-Mail reminder to Users", - cron = "0 0 7 ? * MON-FRI") - void sendReminder() { - reminderEmailSender.sendReminder(); - } - - @Scheduled( - identity = "Receive E-Mails sent to employees from ZEP", - cron = "${mega.mail.receiver.cron-expr}" - ) - void receiveMails() { - mailReceiver.retrieveZepEmailsFromInbox(); - } - - @Scheduled(identity = "Take existing PrematureEmployeeChecks and update StepEntries accordingly on the last day of a month at 06:00", - cron = "0 0 6 L * ? *") - void syncPrematureEmployeeChecksWithStepEntries() { - prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries(DateUtils.getCurrentYearMonth()); - } - - @Scheduled(identity = "Set state of step id 1 to DONE for any employee who was absent the whole month and had no time bookings - on the first day of a month at 07:00", - cron = "0 0 7 1 * ? *") - void setStateToDoneForEmployeesWhoWhereAbsentWholeMonth(){ - syncService.syncUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth(); - } -} diff --git a/src/main/java/com/gepardec/mega/application/schedule/SyncSchedules.java b/src/main/java/com/gepardec/mega/application/schedule/SyncSchedules.java new file mode 100644 index 000000000..8bbcabc75 --- /dev/null +++ b/src/main/java/com/gepardec/mega/application/schedule/SyncSchedules.java @@ -0,0 +1,83 @@ +package com.gepardec.mega.application.schedule; + +import com.gepardec.mega.service.api.EnterpriseSyncService; +import com.gepardec.mega.service.api.PrematureEmployeeCheckSyncService; +import com.gepardec.mega.service.api.ProjectSyncService; +import com.gepardec.mega.service.api.StepEntrySyncService; +import com.gepardec.mega.service.api.SyncService; +import io.quarkus.scheduler.Scheduled; +import jakarta.enterprise.context.Dependent; +import jakarta.inject.Inject; + +import java.util.concurrent.TimeUnit; + +import static com.gepardec.mega.domain.utils.DateUtils.getFirstDayOfCurrentMonth; + +@Dependent +public class SyncSchedules { + + @Inject + SyncService syncService; + + @Inject + StepEntrySyncService stepEntrySyncService; + + @Inject + ProjectSyncService projectSyncService; + + @Inject + EnterpriseSyncService enterpriseSyncService; + + @Inject + PrematureEmployeeCheckSyncService prematureEmployeeCheckSyncService; + + @Scheduled( + identity = "Sync ZEP-Employees with Users in the database every 30 minutes", + every = "PT30M", + delay = 15, delayUnit = TimeUnit.SECONDS // We need to wait for liquibase to finish, but is executed in parallel + ) + void syncEmployees() { + syncService.syncEmployees(); + } + + @Scheduled( + identity = "Set state of step id 1 to DONE for any employee who was absent the whole month and had no time bookings - on the first day of a month at 07:00", + cron = "0 0 7 1 * ? *" + ) + void setStateToDoneForEmployeesWhoWhereAbsentWholeMonth() { + syncService.syncUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth(); + } + + @Scheduled( + identity = "Generate step entries on the last day of a month at 00:00", + cron = "0 0 0 L * ? *" + ) + void generateStepEntries() { + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); + } + + @Scheduled( + identity = "Update project entries every 30 minutes", + every = "PT30M", + delay = 30, delayUnit = TimeUnit.SECONDS + ) + void generateProjects() { + projectSyncService.generateProjects(getFirstDayOfCurrentMonth()); + } + + @Scheduled( + identity = "Generate enterprise entries on the last day of a month at 00:00", + cron = "0 0 0 L * ? *" + ) + void generateEnterpriseEntries() { + enterpriseSyncService.generateEnterpriseEntries(getFirstDayOfCurrentMonth()); + } + + @Scheduled( + identity = "Take existing PrematureEmployeeChecks and update StepEntries accordingly on the last day of a month at 06:00", + cron = "0 0 6 L * ? *" + ) + void syncPrematureEmployeeChecksWithStepEntries() { + prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries(getFirstDayOfCurrentMonth()); + } +} diff --git a/src/main/java/com/gepardec/mega/db/entity/employee/PrematureEmployeeCheckEntity.java b/src/main/java/com/gepardec/mega/db/entity/employee/PrematureEmployeeCheckEntity.java index 35a1259c6..943a9b0b6 100644 --- a/src/main/java/com/gepardec/mega/db/entity/employee/PrematureEmployeeCheckEntity.java +++ b/src/main/java/com/gepardec/mega/db/entity/employee/PrematureEmployeeCheckEntity.java @@ -31,7 +31,7 @@ public class PrematureEmployeeCheckEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "PrematureEmployeeCheck_GEN") - @SequenceGenerator(name = "PrematureEmployeeCheck_GEN", sequenceName = "PrematureEmployeeCheck_SEQ") + @SequenceGenerator(name = "PrematureEmployeeCheck_GEN", sequenceName = "PrematureEmployeeCheck_SEQ", allocationSize = 1) @Column(name = "id", nullable = false) private Long id; diff --git a/src/main/java/com/gepardec/mega/domain/utils/DateUtils.java b/src/main/java/com/gepardec/mega/domain/utils/DateUtils.java index 91ee96745..d85253db7 100644 --- a/src/main/java/com/gepardec/mega/domain/utils/DateUtils.java +++ b/src/main/java/com/gepardec/mega/domain/utils/DateUtils.java @@ -104,8 +104,7 @@ public static LocalDate getFirstDayOfCurrentMonth() { return LocalDate.now().with(TemporalAdjusters.firstDayOfMonth()); } - public static YearMonth getCurrentYearMonth() { - LocalDate today = today(); - return YearMonth.of(today().getYear(), today.getMonth()); + public static LocalDate getFirstOfYearMonth(YearMonth yearMonth) { + return yearMonth.atDay(1); } } diff --git a/src/main/java/com/gepardec/mega/notification/mail/ReminderEmailSender.java b/src/main/java/com/gepardec/mega/notification/mail/ReminderEmailSender.java index 7c95848ff..117605799 100644 --- a/src/main/java/com/gepardec/mega/notification/mail/ReminderEmailSender.java +++ b/src/main/java/com/gepardec/mega/notification/mail/ReminderEmailSender.java @@ -111,7 +111,7 @@ void sendReminderToUser() { .forEach(user -> mailSender.send(EMPLOYEE_CHECK_PROJECTTIME, user.getEmail(), user.getFirstname(), applicationConfig.getDefaultLocale())); logSentNotification(EMPLOYEE_CHECK_PROJECTTIME); } else { - logger.info("NO Reminder to employes sent, cause mega.mail.employees.notification-property is false"); + logger.info("NO Reminder to employees sent, cause mega.mail.employees.notification-property is false"); } } diff --git a/src/main/java/com/gepardec/mega/rest/api/MailResource.java b/src/main/java/com/gepardec/mega/rest/api/MailResource.java new file mode 100644 index 000000000..84ff2bb7e --- /dev/null +++ b/src/main/java/com/gepardec/mega/rest/api/MailResource.java @@ -0,0 +1,54 @@ +package com.gepardec.mega.rest.api; + +import io.quarkus.oidc.Tenant; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.security.SecuritySchemes; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +import java.time.LocalDateTime; + +@Path("/mail") +@Tenant("mega-cron") +@Tag(name = "MailResource") +@Produces(MediaType.APPLICATION_JSON) +@SecurityRequirement(name = "mega-cron") +@SecuritySchemes( + @SecurityScheme( + securitySchemeName = "mega-cron", + type = SecuritySchemeType.OAUTH2, + flows = @OAuthFlows(clientCredentials = @OAuthFlow()) + ) +) +public interface MailResource { + + @Operation(operationId = "send-reminder", description = "Sends reminder emails to affected employees.") + @GET + @Path("/send-reminder") + Response sendReminder(); + + /** + * The sole purpose of this endpoint is to trigger the retrieval of emails from the ZEP inbox manually. + * This is useful for testing purposes. + * Therefore, this endpoint must not be used in production! + * + * @return + */ + @Operation(operationId = "retrieve-zep-mails", description = "Trigger email retrieval from mail inbox manually.") + @GET + @Path("/retrieve-zep-mails") + Response retrieveZepEmailsFromInbox(); + + @Path("/ping") + @GET + LocalDateTime ping(); +} diff --git a/src/main/java/com/gepardec/mega/rest/api/PubSubResource.java b/src/main/java/com/gepardec/mega/rest/api/PubSubResource.java new file mode 100644 index 000000000..e6f5936f7 --- /dev/null +++ b/src/main/java/com/gepardec/mega/rest/api/PubSubResource.java @@ -0,0 +1,29 @@ +package com.gepardec.mega.rest.api; + +import io.quarkus.oidc.Tenant; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; +import org.eclipse.microprofile.openapi.annotations.Operation; + +import java.time.LocalDateTime; + +@Tenant("pubsub") +@Path("/pubsub") +public interface PubSubResource { + + /** + * This endpoint serves as a webhook for new emails from ZEP to trigger comment creation. + * A Google Cloud Pub/Sub subscription is set up to call this endpoint when a new email is received. + * + * @return + */ + @Operation(operationId = "gmailMessageReceivedWebhook", description = "Webhook for new emails from ZEP to trigger comment creation.") + @POST + @Path("/message-received") + Response gmailMessageReceivedWebhook(String payload); + + @Path("/ping") + @POST + LocalDateTime ping(String payload); +} diff --git a/src/main/java/com/gepardec/mega/rest/api/SyncResource.java b/src/main/java/com/gepardec/mega/rest/api/SyncResource.java index b1d96fc09..0f2377a3c 100644 --- a/src/main/java/com/gepardec/mega/rest/api/SyncResource.java +++ b/src/main/java/com/gepardec/mega/rest/api/SyncResource.java @@ -1,6 +1,7 @@ package com.gepardec.mega.rest.api; import com.gepardec.mega.rest.model.EmployeeDto; +import io.quarkus.oidc.Tenant; import jakarta.ws.rs.GET; import jakarta.ws.rs.PUT; import jakarta.ws.rs.Path; @@ -11,18 +12,34 @@ import org.eclipse.microprofile.openapi.annotations.Operation; import org.eclipse.microprofile.openapi.annotations.enums.ParameterIn; import org.eclipse.microprofile.openapi.annotations.enums.SchemaType; +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; import org.eclipse.microprofile.openapi.annotations.media.Content; import org.eclipse.microprofile.openapi.annotations.media.Schema; import org.eclipse.microprofile.openapi.annotations.parameters.Parameter; import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.security.SecuritySchemes; import org.eclipse.microprofile.openapi.annotations.tags.Tag; +import java.time.LocalDateTime; import java.time.YearMonth; import java.util.List; @Path("/sync") +@Tenant("mega-cron") @Tag(name = "SyncResource") @Produces(MediaType.APPLICATION_JSON) +@SecurityRequirement(name = "mega-cron") +@SecuritySchemes( + @SecurityScheme( + securitySchemeName = "mega-cron", + type = SecuritySchemeType.OAUTH2, + flows = @OAuthFlows(clientCredentials = @OAuthFlow()) + ) +) public interface SyncResource { @Operation(operationId = "syncProjects", description = "Syncs projects for a given amount of months.") @@ -121,4 +138,8 @@ public interface SyncResource { @Path("/automatic-release") @PUT List updateEmployeesWithoutTimeBookingsAndAbsentWholeMonth(); + + @Path("/ping") + @GET + LocalDateTime ping(); } diff --git a/src/main/java/com/gepardec/mega/rest/api/WatchInboxResource.java b/src/main/java/com/gepardec/mega/rest/api/WatchInboxResource.java new file mode 100644 index 000000000..6c60b049c --- /dev/null +++ b/src/main/java/com/gepardec/mega/rest/api/WatchInboxResource.java @@ -0,0 +1,43 @@ +package com.gepardec.mega.rest.api; + +import com.google.api.services.gmail.model.WatchResponse; +import io.quarkus.oidc.Tenant; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.MediaType; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.eclipse.microprofile.openapi.annotations.enums.SecuritySchemeType; +import org.eclipse.microprofile.openapi.annotations.responses.APIResponse; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlow; +import org.eclipse.microprofile.openapi.annotations.security.OAuthFlows; +import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement; +import org.eclipse.microprofile.openapi.annotations.security.SecurityScheme; +import org.eclipse.microprofile.openapi.annotations.security.SecuritySchemes; +import org.eclipse.microprofile.openapi.annotations.tags.Tag; + +@Path("/watch-inbox") +@Tenant("mega-cron") +@Tag(name = "WatchInboxResource") +@Produces(MediaType.APPLICATION_JSON) +@SecurityRequirement(name = "mega-cron") +@SecuritySchemes( + @SecurityScheme( + securitySchemeName = "mega-cron", + type = SecuritySchemeType.OAUTH2, + flows = @OAuthFlows(clientCredentials = @OAuthFlow()) + ) +) +public interface WatchInboxResource { + + @POST + @Path("/watch") + @Operation(operationId = "watch", description = "Send a watch request to the Gmail API to receive notifications for new emails.") + WatchResponse watchInbox(); + + @POST + @Path("/stop") + @Operation(operationId = "stop", description = "Stop watching the inbox.") + @APIResponse(responseCode = "204", description = "Watch stopped successfully") + Void stopInbox(); +} diff --git a/src/main/java/com/gepardec/mega/rest/impl/EmployeeResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/EmployeeResourceImpl.java index 18c428d09..a9f044191 100644 --- a/src/main/java/com/gepardec/mega/rest/impl/EmployeeResourceImpl.java +++ b/src/main/java/com/gepardec/mega/rest/impl/EmployeeResourceImpl.java @@ -1,6 +1,6 @@ package com.gepardec.mega.rest.impl; -import com.gepardec.mega.application.interceptor.RolesAllowed; +import com.gepardec.mega.application.interceptor.MegaRolesAllowed; import com.gepardec.mega.domain.model.Employee; import com.gepardec.mega.domain.model.Role; import com.gepardec.mega.rest.api.EmployeeResource; @@ -15,7 +15,7 @@ @RequestScoped @Authenticated -@RolesAllowed({Role.PROJECT_LEAD, Role.OFFICE_MANAGEMENT}) +@MegaRolesAllowed({Role.PROJECT_LEAD, Role.OFFICE_MANAGEMENT}) public class EmployeeResourceImpl implements EmployeeResource { @Inject diff --git a/src/main/java/com/gepardec/mega/rest/impl/EnterpriseResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/EnterpriseResourceImpl.java index 17df31111..02bebee19 100644 --- a/src/main/java/com/gepardec/mega/rest/impl/EnterpriseResourceImpl.java +++ b/src/main/java/com/gepardec/mega/rest/impl/EnterpriseResourceImpl.java @@ -1,6 +1,6 @@ package com.gepardec.mega.rest.impl; -import com.gepardec.mega.application.interceptor.RolesAllowed; +import com.gepardec.mega.application.interceptor.MegaRolesAllowed; import com.gepardec.mega.domain.model.Role; import com.gepardec.mega.rest.api.EnterpriseResource; import com.gepardec.mega.rest.model.EnterpriseEntryDto; @@ -15,7 +15,7 @@ @RequestScoped @Authenticated -@RolesAllowed(value = {Role.PROJECT_LEAD, Role.OFFICE_MANAGEMENT}) +@MegaRolesAllowed(value = {Role.PROJECT_LEAD, Role.OFFICE_MANAGEMENT}) public class EnterpriseResourceImpl implements EnterpriseResource { @Inject diff --git a/src/main/java/com/gepardec/mega/rest/impl/MailResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/MailResourceImpl.java new file mode 100644 index 000000000..fa2f3144c --- /dev/null +++ b/src/main/java/com/gepardec/mega/rest/impl/MailResourceImpl.java @@ -0,0 +1,55 @@ +package com.gepardec.mega.rest.impl; + +import com.gepardec.mega.notification.mail.ReminderEmailSender; +import com.gepardec.mega.notification.mail.receiver.MailReceiver; +import com.gepardec.mega.rest.api.MailResource; +import jakarta.annotation.security.RolesAllowed; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; +import org.slf4j.Logger; + +import java.time.LocalDateTime; + +@RequestScoped +@RolesAllowed("mega-cron:mail") +public class MailResourceImpl implements MailResource { + + @Inject + ReminderEmailSender reminderEmailSender; + + @Inject + MailReceiver mailReceiver; + + @Inject + Logger logger; + + @Override + public Response sendReminder() { + try { + reminderEmailSender.sendReminder(); + } catch (Exception e) { + logger.error(e.getMessage()); + return Response.serverError().entity(e.getMessage()).build(); + } + + return Response.ok().build(); + } + + @Override + public Response retrieveZepEmailsFromInbox() { + try { + mailReceiver.retrieveZepEmailsFromInbox(); + } catch (Exception e) { + logger.error(e.getMessage()); + return Response.serverError().entity(e.getMessage()).build(); + } + + return Response.ok().build(); + } + + @Override + public LocalDateTime ping() { + return LocalDateTime.now(); + } +} diff --git a/src/main/java/com/gepardec/mega/rest/impl/ManagementResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/ManagementResourceImpl.java index af6df3007..eca387380 100644 --- a/src/main/java/com/gepardec/mega/rest/impl/ManagementResourceImpl.java +++ b/src/main/java/com/gepardec/mega/rest/impl/ManagementResourceImpl.java @@ -1,6 +1,6 @@ package com.gepardec.mega.rest.impl; -import com.gepardec.mega.application.interceptor.RolesAllowed; +import com.gepardec.mega.application.interceptor.MegaRolesAllowed; import com.gepardec.mega.db.entity.employee.EmployeeState; import com.gepardec.mega.db.entity.employee.StepEntry; import com.gepardec.mega.db.entity.employee.User; @@ -37,7 +37,7 @@ @RequestScoped @Authenticated -@RolesAllowed(value = {Role.PROJECT_LEAD, Role.OFFICE_MANAGEMENT}) +@MegaRolesAllowed(value = {Role.PROJECT_LEAD, Role.OFFICE_MANAGEMENT}) public class ManagementResourceImpl implements ManagementResource { private static final String DATE_FORMAT_PATTERN = "yyyy-MM-dd"; diff --git a/src/main/java/com/gepardec/mega/rest/impl/PubSubResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/PubSubResourceImpl.java new file mode 100644 index 000000000..698a11ae9 --- /dev/null +++ b/src/main/java/com/gepardec/mega/rest/impl/PubSubResourceImpl.java @@ -0,0 +1,43 @@ +package com.gepardec.mega.rest.impl; + +import com.gepardec.mega.notification.mail.receiver.MailReceiver; +import com.gepardec.mega.rest.api.PubSubResource; +import io.quarkus.security.Authenticated; +import jakarta.inject.Inject; +import jakarta.ws.rs.core.Response; +import org.slf4j.Logger; + +import java.time.LocalDateTime; + +@Authenticated +public class PubSubResourceImpl implements PubSubResource { + + @Inject + Logger logger; + + @Inject + MailReceiver mailReceiver; + + @Override + public Response gmailMessageReceivedWebhook(String payload) { + try { + logNotificationReceived(payload); + mailReceiver.retrieveZepEmailsFromInbox(); + } catch (Exception e) { + logger.error(e.getMessage()); + return Response.serverError().entity(e.getMessage()).build(); + } + + return Response.ok().build(); + } + + @Override + public LocalDateTime ping(String payload) { + logNotificationReceived(payload); + return LocalDateTime.now(); + } + + private void logNotificationReceived(String payload) { + logger.info("Received notification from Pub/Sub: {}", payload != null ? payload.strip() : null); + } +} diff --git a/src/main/java/com/gepardec/mega/rest/impl/SyncResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/SyncResourceImpl.java index bb15f4871..700ac16c2 100644 --- a/src/main/java/com/gepardec/mega/rest/impl/SyncResourceImpl.java +++ b/src/main/java/com/gepardec/mega/rest/impl/SyncResourceImpl.java @@ -1,6 +1,5 @@ package com.gepardec.mega.rest.impl; -import com.gepardec.mega.domain.utils.DateUtils; import com.gepardec.mega.rest.api.SyncResource; import com.gepardec.mega.rest.model.EmployeeDto; import com.gepardec.mega.service.api.EnterpriseSyncService; @@ -9,16 +8,23 @@ import com.gepardec.mega.service.api.StepEntrySyncService; import com.gepardec.mega.service.api.SyncService; import io.quarkus.arc.properties.IfBuildProperty; +import jakarta.annotation.security.RolesAllowed; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import jakarta.ws.rs.core.Response; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.YearMonth; import java.util.List; +import java.util.function.Function; + +import static com.gepardec.mega.domain.utils.DateUtils.getFirstDayOfCurrentMonth; +import static com.gepardec.mega.domain.utils.DateUtils.getFirstOfYearMonth; @RequestScoped @IfBuildProperty(name = "mega.endpoint.test.enable", stringValue = "true", enableIfMissing = true) +@RolesAllowed("mega-cron:sync") public class SyncResourceImpl implements SyncResource { @Inject @@ -36,27 +42,6 @@ public class SyncResourceImpl implements SyncResource { @Inject PrematureEmployeeCheckSyncService prematureEmployeeCheckSyncService; - - - - @Override - public Response syncProjects(YearMonth from, YearMonth to) { - if (from == null) { - return Response.ok(projectSyncService.generateProjects()).build(); - } - - if (to == null) { - return Response.ok(projectSyncService.generateProjects(from.atDay(1))).build(); - } - - while (from.isBefore(to)) { - projectSyncService.generateProjects(from.atDay(1)); - from = from.plusMonths(1); - } - - return Response.ok(projectSyncService.generateProjects(from.atDay(1))).build(); - } - @Override public Response syncEmployees() { syncService.syncEmployees(); @@ -64,57 +49,23 @@ public Response syncEmployees() { } @Override - public Response generateEnterpriseEntries(YearMonth from, YearMonth to) { - if (from == null) { - return Response.ok(enterpriseSyncService.generateEnterpriseEntries(LocalDate.now().withDayOfMonth(1))).build(); - } - if (to == null) { - return Response.ok(enterpriseSyncService.generateEnterpriseEntries(from.atDay(1))).build(); - } - - while (from.isBefore(to)) { - enterpriseSyncService.generateEnterpriseEntries(from.atDay(1)); - from = from.plusMonths(1); - } + public Response syncProjects(YearMonth from, YearMonth to) { + return syncFromTo(projectSyncService::generateProjects, from, to); + } - return Response.ok(enterpriseSyncService.generateEnterpriseEntries(from.atDay(1))).build(); + @Override + public Response generateEnterpriseEntries(YearMonth from, YearMonth to) { + return syncFromTo(enterpriseSyncService::generateEnterpriseEntries, from, to); } @Override public Response generateStepEntries(YearMonth from, YearMonth to) { - if (from == null) { - return Response.ok(stepEntrySyncService.generateStepEntriesFromEndpoint()).build(); - } - if (to == null) { - return Response.ok(stepEntrySyncService.generateStepEntriesFromEndpoint(from)).build(); - } - - while (from.isBefore(to)) { - stepEntrySyncService.generateStepEntriesFromEndpoint(from); - from = from.plusMonths(1); - } - - return Response.ok(stepEntrySyncService.generateStepEntriesFromEndpoint(from)).build(); + return syncFromTo(stepEntrySyncService::generateStepEntries, from, to); } @Override public Response syncPrematureEmployeeChecks(YearMonth from, YearMonth to) { - if (from == null) { - prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries(DateUtils.getCurrentYearMonth()); - return Response.ok().build(); - } - - if (to == null) { - prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries(from); - return Response.ok().build(); - } - - do { - prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries(from); - from = from.plusMonths(1); - } while (from.compareTo(to) <= 0); - - return Response.ok().build(); + return syncFromTo(prematureEmployeeCheckSyncService::syncPrematureEmployeeChecksWithStepEntries, from, to); } @Override @@ -131,4 +82,26 @@ public Response syncAll(YearMonth from, YearMonth to) { public List updateEmployeesWithoutTimeBookingsAndAbsentWholeMonth() { return syncService.syncUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth(); } + + @Override + public LocalDateTime ping() { + return LocalDateTime.now(); + } + + private Response syncFromTo(Function syncFunction, YearMonth from, YearMonth to) { + if (from == null) { + return Response.ok(syncFunction.apply(getFirstDayOfCurrentMonth())).build(); + } + + if (to == null) { + return Response.ok(syncFunction.apply(getFirstOfYearMonth(from))).build(); + } + + while (from.isBefore(to)) { + syncFunction.apply(getFirstOfYearMonth(from)); + from = from.plusMonths(1); + } + + return Response.ok(syncFunction.apply(getFirstOfYearMonth(from))).build(); + } } diff --git a/src/main/java/com/gepardec/mega/rest/impl/UserResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/UserResourceImpl.java index 7afe26149..881a7af66 100644 --- a/src/main/java/com/gepardec/mega/rest/impl/UserResourceImpl.java +++ b/src/main/java/com/gepardec/mega/rest/impl/UserResourceImpl.java @@ -1,6 +1,6 @@ package com.gepardec.mega.rest.impl; -import com.gepardec.mega.application.interceptor.RolesAllowed; +import com.gepardec.mega.application.interceptor.MegaRolesAllowed; import com.gepardec.mega.domain.model.Role; import com.gepardec.mega.domain.model.UserContext; import com.gepardec.mega.rest.api.UserResource; @@ -13,7 +13,7 @@ @RequestScoped @Authenticated -@RolesAllowed(Role.EMPLOYEE) +@MegaRolesAllowed(Role.EMPLOYEE) public class UserResourceImpl implements UserResource { @Inject diff --git a/src/main/java/com/gepardec/mega/rest/impl/WatchInboxResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/WatchInboxResourceImpl.java new file mode 100644 index 000000000..5e93bc244 --- /dev/null +++ b/src/main/java/com/gepardec/mega/rest/impl/WatchInboxResourceImpl.java @@ -0,0 +1,26 @@ +package com.gepardec.mega.rest.impl; + +import com.gepardec.mega.rest.api.WatchInboxResource; +import com.gepardec.mega.service.api.GmailService; +import com.google.api.services.gmail.model.WatchResponse; +import jakarta.annotation.security.RolesAllowed; +import jakarta.enterprise.context.RequestScoped; +import jakarta.inject.Inject; + +@RequestScoped +@RolesAllowed("mega-cron:mail") +public class WatchInboxResourceImpl implements WatchInboxResource { + + @Inject + GmailService gmailService; + + @Override + public WatchResponse watchInbox() { + return gmailService.watchInbox(); + } + + @Override + public Void stopInbox() { + return gmailService.stopWatchingInbox(); + } +} diff --git a/src/main/java/com/gepardec/mega/rest/impl/WorkerResourceImpl.java b/src/main/java/com/gepardec/mega/rest/impl/WorkerResourceImpl.java index 4bd08ea1c..290bd80ac 100644 --- a/src/main/java/com/gepardec/mega/rest/impl/WorkerResourceImpl.java +++ b/src/main/java/com/gepardec/mega/rest/impl/WorkerResourceImpl.java @@ -1,6 +1,6 @@ package com.gepardec.mega.rest.impl; -import com.gepardec.mega.application.interceptor.RolesAllowed; +import com.gepardec.mega.application.interceptor.MegaRolesAllowed; import com.gepardec.mega.db.entity.common.AbsenceType; import com.gepardec.mega.domain.model.AbsenceTime; import com.gepardec.mega.domain.model.Employee; @@ -47,7 +47,7 @@ @RequestScoped @Authenticated -@RolesAllowed(Role.EMPLOYEE) +@MegaRolesAllowed(Role.EMPLOYEE) public class WorkerResourceImpl implements WorkerResource { @Inject diff --git a/src/main/java/com/gepardec/mega/service/api/GmailService.java b/src/main/java/com/gepardec/mega/service/api/GmailService.java new file mode 100644 index 000000000..8096aae0d --- /dev/null +++ b/src/main/java/com/gepardec/mega/service/api/GmailService.java @@ -0,0 +1,11 @@ +package com.gepardec.mega.service.api; + + +import com.google.api.services.gmail.model.WatchResponse; + +public interface GmailService { + + WatchResponse watchInbox(); + + Void stopWatchingInbox(); +} diff --git a/src/main/java/com/gepardec/mega/service/api/PrematureEmployeeCheckSyncService.java b/src/main/java/com/gepardec/mega/service/api/PrematureEmployeeCheckSyncService.java index 8d0af5357..15c7349ab 100644 --- a/src/main/java/com/gepardec/mega/service/api/PrematureEmployeeCheckSyncService.java +++ b/src/main/java/com/gepardec/mega/service/api/PrematureEmployeeCheckSyncService.java @@ -1,8 +1,8 @@ package com.gepardec.mega.service.api; -import java.time.YearMonth; +import java.time.LocalDate; public interface PrematureEmployeeCheckSyncService { - boolean syncPrematureEmployeeChecksWithStepEntries(YearMonth yearMonth); + boolean syncPrematureEmployeeChecksWithStepEntries(LocalDate date); } diff --git a/src/main/java/com/gepardec/mega/service/api/ProjectSyncService.java b/src/main/java/com/gepardec/mega/service/api/ProjectSyncService.java index 3550a7107..c0c22f61e 100644 --- a/src/main/java/com/gepardec/mega/service/api/ProjectSyncService.java +++ b/src/main/java/com/gepardec/mega/service/api/ProjectSyncService.java @@ -4,7 +4,5 @@ public interface ProjectSyncService { - boolean generateProjects(); - boolean generateProjects(LocalDate date); } diff --git a/src/main/java/com/gepardec/mega/service/api/StepEntrySyncService.java b/src/main/java/com/gepardec/mega/service/api/StepEntrySyncService.java index 148edc0ad..38862d032 100644 --- a/src/main/java/com/gepardec/mega/service/api/StepEntrySyncService.java +++ b/src/main/java/com/gepardec/mega/service/api/StepEntrySyncService.java @@ -1,12 +1,8 @@ package com.gepardec.mega.service.api; -import java.time.YearMonth; +import java.time.LocalDate; public interface StepEntrySyncService { - boolean generateStepEntriesFromEndpoint(); - - boolean generateStepEntriesFromEndpoint(YearMonth date); - - boolean generateStepEntriesFromScheduler(); + boolean generateStepEntries(LocalDate date); } diff --git a/src/main/java/com/gepardec/mega/service/impl/GmailServiceImpl.java b/src/main/java/com/gepardec/mega/service/impl/GmailServiceImpl.java new file mode 100644 index 000000000..0f16ca73e --- /dev/null +++ b/src/main/java/com/gepardec/mega/service/impl/GmailServiceImpl.java @@ -0,0 +1,86 @@ +package com.gepardec.mega.service.impl; + +import com.gepardec.mega.application.configuration.GoogleCloudConfig; +import com.gepardec.mega.service.api.GmailService; +import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; +import com.google.api.client.json.gson.GsonFactory; +import com.google.api.services.gmail.Gmail; +import com.google.api.services.gmail.model.WatchRequest; +import com.google.api.services.gmail.model.WatchResponse; +import com.google.auth.http.HttpCredentialsAdapter; +import com.google.auth.oauth2.GoogleCredentials; +import jakarta.enterprise.context.Dependent; +import jakarta.inject.Inject; +import org.slf4j.Logger; + +import java.io.IOException; +import java.security.GeneralSecurityException; + +@Dependent +public class GmailServiceImpl implements GmailService { + + @Inject + GoogleCredentials googleCredentials; + + @Inject + GoogleCloudConfig googleCloudConfig; + + @Inject + Logger logger; + + @Override + public WatchResponse watchInbox() { + try { + // Build Gmail service + Gmail gmailService = + initializeGmailBuilder() + .setApplicationName(googleCloudConfig.getApplicationName()) + .build(); + + // Create Watch Request + WatchRequest watchRequest = new WatchRequest(); + watchRequest.setLabelFilterBehavior("INCLUDE"); + watchRequest.setLabelIds(googleCloudConfig.getLabelFilter()); + watchRequest.setTopicName(googleCloudConfig.getTopicName()); + + // Execute Watch Request + var res = gmailService.users().watch(googleCloudConfig.getWorkspaceUser(), watchRequest).execute(); + logger.info("Watch Response: {}", res); + + return res; + } catch (IOException | GeneralSecurityException e) { + logger.error(e.getMessage()); + } + + return null; + } + + @Override + public Void stopWatchingInbox() { + try { + // Build Gmail service + Gmail gmailService = + initializeGmailBuilder() + .setApplicationName(googleCloudConfig.getApplicationName()) + .build(); + + // Execute Stop Watch Request + var res = gmailService.users().stop(googleCloudConfig.getWorkspaceUser()).execute(); + logger.info("Stop Response: {}", res); + + return res; + } catch (IOException | GeneralSecurityException e) { + logger.error(e.getMessage()); + } + + return null; + } + + private Gmail.Builder initializeGmailBuilder() throws GeneralSecurityException, IOException { + return new Gmail.Builder( + GoogleNetHttpTransport.newTrustedTransport(), + GsonFactory.getDefaultInstance(), + new HttpCredentialsAdapter(googleCredentials) + ); + } +} diff --git a/src/main/java/com/gepardec/mega/service/impl/PrematureEmployeeCheckSyncServiceImpl.java b/src/main/java/com/gepardec/mega/service/impl/PrematureEmployeeCheckSyncServiceImpl.java index 715d8862f..3a4836f70 100644 --- a/src/main/java/com/gepardec/mega/service/impl/PrematureEmployeeCheckSyncServiceImpl.java +++ b/src/main/java/com/gepardec/mega/service/impl/PrematureEmployeeCheckSyncServiceImpl.java @@ -13,7 +13,6 @@ import org.slf4j.Logger; import java.time.LocalDate; -import java.time.YearMonth; import java.util.List; import java.util.stream.Collectors; @@ -33,10 +32,9 @@ public class PrematureEmployeeCheckSyncServiceImpl implements PrematureEmployeeC Logger logger; @Override - public boolean syncPrematureEmployeeChecksWithStepEntries(YearMonth yearMonth) { + public boolean syncPrematureEmployeeChecksWithStepEntries(LocalDate date) { boolean allEntriesUpdated = true; - LocalDate selectedMonth = yearMonth.atDay(1); - List prematureEmployeeCheckEntities = prematureEmployeeCheckService.findAllForMonth(selectedMonth) + List prematureEmployeeCheckEntities = prematureEmployeeCheckService.findAllForMonth(date) .stream() .filter(pec -> pec.getState().equals(PrematureEmployeeCheckState.DONE) || pec.getState().equals(PrematureEmployeeCheckState.IN_PROGRESS)) .toList(); @@ -44,7 +42,7 @@ public boolean syncPrematureEmployeeChecksWithStepEntries(YearMonth yearMonth) { logger.info( String.format("Syncing %s PrematureEmployeeChecks with StepEntries for Month: %s", prematureEmployeeCheckEntities.size(), - selectedMonth) + date) ); for (PrematureEmployeeCheck pec : prematureEmployeeCheckEntities) { @@ -63,7 +61,7 @@ public boolean syncPrematureEmployeeChecksWithStepEntries(YearMonth yearMonth) { } } - prematureEmployeeCheckService.deleteAllForMonthWithState(selectedMonth, List.of(PrematureEmployeeCheckState.CANCELLED, PrematureEmployeeCheckState.IN_PROGRESS)); + prematureEmployeeCheckService.deleteAllForMonthWithState(date, List.of(PrematureEmployeeCheckState.CANCELLED, PrematureEmployeeCheckState.IN_PROGRESS)); return allEntriesUpdated; } diff --git a/src/main/java/com/gepardec/mega/service/impl/ProjectSyncServiceImpl.java b/src/main/java/com/gepardec/mega/service/impl/ProjectSyncServiceImpl.java index 7de7c1068..9eeb27a63 100644 --- a/src/main/java/com/gepardec/mega/service/impl/ProjectSyncServiceImpl.java +++ b/src/main/java/com/gepardec/mega/service/impl/ProjectSyncServiceImpl.java @@ -75,12 +75,6 @@ public boolean generateProjects(LocalDate date) { return !projects.isEmpty(); } - - @Override - public boolean generateProjects() { - return generateProjects(LocalDate.now().minusMonths(1).withDayOfMonth(1)); - } - private List createProjects(List activeUsers, List projects, LocalDate date) { return projects.stream() .map(project -> createProjectEntityFromProject(activeUsers, project, date)) diff --git a/src/main/java/com/gepardec/mega/service/impl/StepEntrySyncServiceImpl.java b/src/main/java/com/gepardec/mega/service/impl/StepEntrySyncServiceImpl.java index 0e7f7de3e..7f5b21da9 100644 --- a/src/main/java/com/gepardec/mega/service/impl/StepEntrySyncServiceImpl.java +++ b/src/main/java/com/gepardec/mega/service/impl/StepEntrySyncServiceImpl.java @@ -51,22 +51,7 @@ public class StepEntrySyncServiceImpl implements StepEntrySyncService { NotificationConfig notificationConfig; @Override - public boolean generateStepEntriesFromEndpoint() { - return generateStepEntries(LocalDate.now().minusMonths(1).with(TemporalAdjusters.firstDayOfMonth())); - } - - @Override - public boolean generateStepEntriesFromEndpoint(YearMonth date) { - return generateStepEntries(date.atDay(1)); - } - - @Override - public boolean generateStepEntriesFromScheduler() { - return generateStepEntries(LocalDate.now().with(TemporalAdjusters.firstDayOfMonth())); - } - - - private boolean generateStepEntries(LocalDate date) { + public boolean generateStepEntries(LocalDate date) { final StopWatch stopWatch = new StopWatch(); stopWatch.start(); diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 136b2ce8c..8eaca4883 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -14,11 +14,11 @@ quarkus: root-path: "/" non-application-root-path: "/" port: 8080 + auth: + proactive: false log: - console: - enable: true - level: "ALL" + level: INFO smallrye-metrics: jaxrs: enabled: true @@ -81,6 +81,35 @@ quarkus: zep-health: url: "${mega.zep.origin}‚" + oidc: + # for the default client (mega) we verify the access token locally + auth-server-url: "${mega.oauth.issuer}" + application-type: "service" + token: + audience: "${mega.oauth.client-id}" + # for the server to server client (mega-cron) we verify the token with token introspection + mega-cron: + auth-server-url: "${mega.oauth.issuer}" + application-type: "service" + token: + require-jwt-introspection-only: true + client-id: "${mega.oauth.mega-cron.client-id}" + credentials: + client-secret: + value: "${MEGA_CRON_CLIENT_SECRET:default}" + roles: + source: accesstoken + role-claim-path: "resource_access/mega-cron/roles" + pubsub: + provider: google + application-type: "service" + token: + verify-access-token-with-user-info: false + +mp: + openapi: + filter: com.gepardec.mega.application.filter.MegaCronSecuritySchemaOASFilter + # disables the DefaultResponseExceptionMapper of the microprofile resteasy client microprofile: rest: @@ -92,8 +121,8 @@ microprofile: mega: info: build: - version: "${revision}" - date: "${timestamp}" + version: "${revision:local}" + date: "${timestamp:2007-12-03T10:15:30}" git: branch: "${BRANCH:local}" @@ -108,12 +137,12 @@ mega: rest-token: "${ZEP_REST_TOKEN:default}" origin: "https://www.zep-online.de/zepgepardecservices_test" - - oauth: client-id: "mega" issuer: "https://gepardec-sso-qa.apps.cloudscale-lpg-2.appuio.cloud/realms/gepardec" scope: "openid profile email microprofile-jwt" + mega-cron: + client-id: "mega-cron" mail: employees: @@ -140,7 +169,7 @@ mega: port: 993 username: "service@gepardec.com" password: "${MAILER_PASSWORD:default}" - sender: "nobody@provantis.de" + sender: "nobody@zep.de" cron-expr: "disabled" personio: @@ -151,13 +180,16 @@ mega: token: expires-in-minutes: 1200 # 20h - real token expires in 24h -mp: - jwt: - verify: - issuer: "${mega.oauth.issuer}" - audiences: "${mega.oauth.client-id}" - publickey: - location: "${mega.oauth.issuer}/protocol/openid-connect/certs" + google: + service-account-key: "${GOOGLE_CLOUD_SERVICE_ACCOUNT_GEPARDEC_MAIL:default}" + gmail: + api: + application-name: "MEGA" + workspace-user: "service@gepardec.com" + label-filter: + - "Label_5632925620975792127" # ID of name "ZEP_TEST" of workspace-user (stage-specific) + pubsub: + topic: "projects/mega-260510/topics/MegaWebhookEmail-QA" #stage-specific ## DEV Properties "%dev": @@ -176,9 +208,6 @@ mp: mega: dash-url: "http://localhost:4444/index.html" - oauth: - issuer: "https://gepardec-sso-qa.apps.cloudscale-lpg-2.appuio.cloud/realms/gepardec" - mail: subject-prefix: "LOCAL: " employees: @@ -221,9 +250,6 @@ mp: zep: origin: "https://www.zep-online.de/zepgepardecservices_test" - oauth: - issuer: "https://gepardec-sso-qa.apps.cloudscale-lpg-2.appuio.cloud/realms/gepardec" - mail: subject-prefix: "UNIT-TEST: " diff --git a/src/test/java/com/gepardec/mega/application/interceptor/RolesAllowedInterceptorTest.java b/src/test/java/com/gepardec/mega/application/interceptor/MegaMegaRolesAllowedInterceptorTest.java similarity index 79% rename from src/test/java/com/gepardec/mega/application/interceptor/RolesAllowedInterceptorTest.java rename to src/test/java/com/gepardec/mega/application/interceptor/MegaMegaRolesAllowedInterceptorTest.java index 2b68f7e91..b765a3448 100644 --- a/src/test/java/com/gepardec/mega/application/interceptor/RolesAllowedInterceptorTest.java +++ b/src/test/java/com/gepardec/mega/application/interceptor/MegaMegaRolesAllowedInterceptorTest.java @@ -22,7 +22,7 @@ import static org.mockito.Mockito.when; @ExtendWith(MockitoExtension.class) -class RolesAllowedInterceptorTest { +class MegaMegaRolesAllowedInterceptorTest { @Mock(answer = Answers.RETURNS_DEEP_STUBS) private UserContext userContext; @@ -31,7 +31,7 @@ class RolesAllowedInterceptorTest { private InvocationContext invocationContext; @InjectMocks - private RolesAllowedInterceptor rolesAllowedInterceptor; + private MegaRolesAllowedInterceptor megaRolesAllowedInterceptor; @Test void invoke_whenAnnotationOnClassLevel_thenUsesClassLevelAnnotation() throws Exception { @@ -39,7 +39,7 @@ void invoke_whenAnnotationOnClassLevel_thenUsesClassLevelAnnotation() throws Exc when(invocationContext.getTarget()).thenReturn(new TargetWithAnnotation()); when(userContext.getUser().getRoles()).thenReturn(Set.of(Role.EMPLOYEE)); - rolesAllowedInterceptor.intercept(invocationContext); + megaRolesAllowedInterceptor.intercept(invocationContext); verify(invocationContext, times(1)).proceed(); } @@ -49,7 +49,7 @@ void invoke_whenNoAnnotationOnMethodAndClassLevel_thenThrowsNullpointerException when(invocationContext.getMethod().getAnnotation(any())).thenReturn(null); when(invocationContext.getTarget()).thenReturn(new TargetNoAnnotation()); - assertThatThrownBy(() -> rolesAllowedInterceptor.intercept(invocationContext)) + assertThatThrownBy(() -> megaRolesAllowedInterceptor.intercept(invocationContext)) .isInstanceOf(NullPointerException.class); } @@ -58,7 +58,7 @@ void invoke_whenNoAnnotationOnMethodAndClassLevel_thenThrowsNullpointerException @Disabled void intercept_whenNotLogged_thenThrowsForbiddenException() { when(invocationContext.getMethod().getAnnotation(any())).thenReturn(createAnnotation(new Role[]{Role.EMPLOYEE})); - assertThatThrownBy(() -> rolesAllowedInterceptor.intercept(invocationContext)).isInstanceOf(ForbiddenException.class); + assertThatThrownBy(() -> megaRolesAllowedInterceptor.intercept(invocationContext)).isInstanceOf(ForbiddenException.class); } @Test @@ -66,7 +66,7 @@ void invoke_whenLoggedAndNotInRole_thenThrowsForbiddenException() { when(invocationContext.getMethod().getAnnotation(any())).thenReturn(createAnnotation(new Role[]{Role.OFFICE_MANAGEMENT})); when(userContext.getUser().getRoles()).thenReturn(Set.of(Role.EMPLOYEE)); - assertThatThrownBy(() -> rolesAllowedInterceptor.intercept(invocationContext)).isInstanceOf(ForbiddenException.class); + assertThatThrownBy(() -> megaRolesAllowedInterceptor.intercept(invocationContext)).isInstanceOf(ForbiddenException.class); } @Test @@ -74,13 +74,13 @@ void invoke_whenLoggedAndInRoleMethodAnnotated_thenThrowsForbiddenException() th when(invocationContext.getMethod().getAnnotation(any())).thenReturn(createAnnotation(Role.values())); when(userContext.getUser().getRoles()).thenReturn(Set.of(Role.EMPLOYEE)); - rolesAllowedInterceptor.intercept(invocationContext); + megaRolesAllowedInterceptor.intercept(invocationContext); verify(invocationContext, times(1)).proceed(); } - private RolesAllowed createAnnotation(final Role[] roles) { - return new RolesAllowed() { + private MegaRolesAllowed createAnnotation(final Role[] roles) { + return new MegaRolesAllowed() { @Override public Role[] value() { return roles; @@ -88,12 +88,12 @@ public Role[] value() { @Override public Class annotationType() { - return RolesAllowed.class; + return MegaRolesAllowed.class; } }; } - @RolesAllowed(Role.EMPLOYEE) + @MegaRolesAllowed(Role.EMPLOYEE) private static class TargetWithAnnotation { } diff --git a/src/test/java/com/gepardec/mega/application/producer/UserContextProducerTest.java b/src/test/java/com/gepardec/mega/application/producer/UserContextProducerTest.java index 5cf24ff49..7f0d912c3 100644 --- a/src/test/java/com/gepardec/mega/application/producer/UserContextProducerTest.java +++ b/src/test/java/com/gepardec/mega/application/producer/UserContextProducerTest.java @@ -3,12 +3,14 @@ import com.gepardec.mega.domain.model.Role; import com.gepardec.mega.domain.model.User; import com.gepardec.mega.domain.model.UserContext; +import com.gepardec.mega.rest.api.UserResource; import com.gepardec.mega.service.api.UserService; import io.quarkus.test.InjectMock; +import io.quarkus.test.common.http.TestHTTPEndpoint; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import jakarta.inject.Inject; import org.junit.jupiter.api.Test; @@ -18,6 +20,7 @@ import static org.mockito.Mockito.when; @QuarkusTest +@TestHTTPEndpoint(UserResource.class) class UserContextProducerTest { @InjectMock @@ -28,7 +31,7 @@ class UserContextProducerTest { @Test @TestSecurity(user = "test") - @JwtSecurity(claims = { + @OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) void createUserContext_whenUserVerified_thenUserSetAndLogged() { diff --git a/src/test/java/com/gepardec/mega/rest/CommentResourceTest.java b/src/test/java/com/gepardec/mega/rest/CommentResourceTest.java index 5b4142ce1..22778c7f2 100644 --- a/src/test/java/com/gepardec/mega/rest/CommentResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/CommentResourceTest.java @@ -12,8 +12,8 @@ import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import io.restassured.common.mapper.TypeRef; import io.restassured.http.ContentType; import jakarta.inject.Inject; @@ -34,7 +34,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) class CommentResourceTest { @@ -57,7 +57,7 @@ void setCommentStatusDone_whenPOST_thenReturnsStatusMETHOD_NOT_ALLOWED() { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void finish_whenUserNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { final User user = createUserForRole(Role.EMPLOYEE); when(userContext.getUser()).thenReturn(user); @@ -99,7 +99,7 @@ void getAllCommentsForEmployee_whenMethodPOST_thenReturnsStatusMETHOD_NOT_ALLOWE @Test @TestSecurity - @JwtSecurity + @OidcSecurity void getAllCommentsForEmployee_whenNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { final User user = createUserForRole(Role.EMPLOYEE); when(userContext.getUser()).thenReturn(user); @@ -167,7 +167,7 @@ void getAllCommentsForEmployee_whenValid_thenReturnsListOfCommentsForEmployee() @Test @TestSecurity - @JwtSecurity + @OidcSecurity void newCommentForEmployee_whenNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { given().contentType(ContentType.JSON) .post("/comments") @@ -228,7 +228,7 @@ void deleteComment_whenIdIsMissing_thenReturnsHttpStatusMethodNotAllowed() { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void deleteComment_whenNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { given().contentType(ContentType.JSON) .delete("/comments/1") @@ -252,7 +252,7 @@ void deleteComment_whenValid_thenReturnsTrue() { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void updateCommentForEmployee_whenNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { given().contentType(ContentType.JSON) .put("/comments") diff --git a/src/test/java/com/gepardec/mega/rest/ConfigDtoResourceTest.java b/src/test/java/com/gepardec/mega/rest/ConfigDtoResourceTest.java index 5a7e9ed35..38c875c31 100644 --- a/src/test/java/com/gepardec/mega/rest/ConfigDtoResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/ConfigDtoResourceTest.java @@ -5,8 +5,8 @@ import com.gepardec.mega.application.configuration.ZepConfig; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import io.restassured.http.ContentType; import jakarta.inject.Inject; import org.apache.http.HttpStatus; @@ -17,7 +17,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) class ConfigDtoResourceTest { diff --git a/src/test/java/com/gepardec/mega/rest/EmployeeResourceTest.java b/src/test/java/com/gepardec/mega/rest/EmployeeResourceTest.java index 13c08d175..dc67786c9 100644 --- a/src/test/java/com/gepardec/mega/rest/EmployeeResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/EmployeeResourceTest.java @@ -9,8 +9,8 @@ import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import io.restassured.common.mapper.TypeRef; import jakarta.inject.Inject; import jakarta.ws.rs.core.MediaType; @@ -29,7 +29,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) class EmployeeResourceTest { @@ -47,7 +47,7 @@ class EmployeeResourceTest { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void list_whenUserNotLoggedAndInRoleOFFICE_MANAGEMENT_thenReturnsHttpStatusUNAUTHORIZED() { when(userContext.getUser()).thenReturn(createUserForRole(Role.OFFICE_MANAGEMENT)); @@ -57,7 +57,7 @@ void list_whenUserNotLoggedAndInRoleOFFICE_MANAGEMENT_thenReturnsHttpStatusUNAUT @Test @TestSecurity - @JwtSecurity + @OidcSecurity void list_whenUserNotLoggedAndInRolePROJECT_LEAD_thenReturnsHttpStatusUNAUTHORIZED() { when(userContext.getUser()).thenReturn(createUserForRole(Role.PROJECT_LEAD)); diff --git a/src/test/java/com/gepardec/mega/rest/ManagementResourceTest.java b/src/test/java/com/gepardec/mega/rest/ManagementResourceTest.java index 9e019ceec..ea3709623 100644 --- a/src/test/java/com/gepardec/mega/rest/ManagementResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/ManagementResourceTest.java @@ -22,8 +22,8 @@ import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import io.restassured.common.mapper.TypeRef; import io.restassured.http.ContentType; import org.apache.http.HttpStatus; @@ -42,7 +42,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) class ManagementResourceTest { @@ -71,7 +71,7 @@ class ManagementResourceTest { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void getAllOfficeManagementEntries_whenNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { when(userContext.getUser()).thenReturn(createUserForRole(Role.OFFICE_MANAGEMENT)); given().contentType(ContentType.JSON) @@ -181,7 +181,7 @@ void getAllOfficeManagementEntries_whenNoStepEntriesFound_thenReturnsEmptyResult @Test @TestSecurity - @JwtSecurity + @OidcSecurity void getAllProjectManagementEntries_whenNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { when(userContext.getUser()).thenReturn(createUserForRole(Role.PROJECT_LEAD)); given().contentType(ContentType.JSON) diff --git a/src/test/java/com/gepardec/mega/rest/PrematureEmployeeCheckResourceTest.java b/src/test/java/com/gepardec/mega/rest/PrematureEmployeeCheckResourceTest.java index b675c0707..30decf4da 100644 --- a/src/test/java/com/gepardec/mega/rest/PrematureEmployeeCheckResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/PrematureEmployeeCheckResourceTest.java @@ -9,8 +9,8 @@ import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import io.restassured.http.ContentType; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -25,7 +25,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) public class PrematureEmployeeCheckResourceTest { @@ -40,7 +40,7 @@ public class PrematureEmployeeCheckResourceTest { @Test @TestSecurity - @JwtSecurity + @OidcSecurity public void add_unauthorized_status401() { // Given when(userContext.getUser()).thenReturn(createUserForRole(Role.EMPLOYEE)); diff --git a/src/test/java/com/gepardec/mega/rest/StepEntryResourceTest.java b/src/test/java/com/gepardec/mega/rest/StepEntryResourceTest.java index c9a8bb257..797049b88 100644 --- a/src/test/java/com/gepardec/mega/rest/StepEntryResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/StepEntryResourceTest.java @@ -10,8 +10,8 @@ import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import io.restassured.http.ContentType; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -27,7 +27,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) class StepEntryResourceTest { @@ -47,7 +47,7 @@ void close_whenPOST_thenReturnsStatusMETHOD_NOT_ALLOWED() { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void close_whenUserNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { final User user = createUserForRole(Role.EMPLOYEE); when(userContext.getUser()).thenReturn(user); diff --git a/src/test/java/com/gepardec/mega/rest/SyncResourceTest.java b/src/test/java/com/gepardec/mega/rest/SyncResourceTest.java index 969fe5dff..93d952fe1 100644 --- a/src/test/java/com/gepardec/mega/rest/SyncResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/SyncResourceTest.java @@ -5,61 +5,63 @@ import com.gepardec.mega.db.entity.employee.StepEntry; import com.gepardec.mega.domain.model.AbsenceTime; import com.gepardec.mega.domain.model.Employee; - - import com.gepardec.mega.domain.utils.DateUtils; import com.gepardec.mega.rest.api.SyncResource; import com.gepardec.mega.rest.model.EmployeeDto; import com.gepardec.mega.service.api.EmployeeService; - import com.gepardec.mega.service.api.StepEntryService; import com.gepardec.mega.zep.ZepService; import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; +import io.quarkus.test.security.TestSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import jakarta.inject.Inject; - import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.mockito.Mockito; import java.time.LocalDate; - import java.util.ArrayList; import java.util.List; - import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; @QuarkusTest +@TestSecurity(user = "test", roles = "mega-cron:sync") +@OidcSecurity(claims = { + @Claim(key = "email", value = "test@gepardec.com") +}) public class SyncResourceTest { + @InjectMock EmployeeService employeeService; - @InjectMock ZepService zepService; @InjectMock StepEntryService stepEntryService; - @Inject SyncResource syncResource; - - @Test - void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNoTimesAndAllAbsences_thenSetStepStateDone() { + void updateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNoTimesAndAllAbsences_thenSetStepStateDone() { Employee userUnderTest = createEmployeeForId("099-testUser", "test.user@gepardec.com", "2024-02-29"); when(employeeService.getAllActiveEmployees()) .thenReturn( - List.of( - createEmployeeForId("e02-testExternal", "external.user@gepardec.com", "2024-02-29"), - userUnderTest, - createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29") - ) + List.of( + createEmployeeForId("e02-testExternal", "external.user@gepardec.com", "2024-02-29"), + userUnderTest, + createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29") + ) ); @@ -83,13 +85,13 @@ void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNo ArgumentCaptor localEndDateCaptor = ArgumentCaptor.forClass(LocalDate.class); when(stepEntryService.setOpenAndAssignedStepEntriesDone( - employeeArgumentCaptor.capture(), - longArgumentCaptor.capture(), - localStartDateCaptor.capture(), - localEndDateCaptor.capture() + employeeArgumentCaptor.capture(), + longArgumentCaptor.capture(), + localStartDateCaptor.capture(), + localEndDateCaptor.capture() ) - ) - .thenReturn(true); + ) + .thenReturn(true); when(stepEntryService.findStepEntryForEmployeeAtStep(anyLong(), anyString(), anyString(), anyString())) .thenReturn(createStepEntry()); @@ -105,17 +107,16 @@ void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNo } - @Test - void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNoTimesAndAllAbsencesWithHomeOfficeAndVacation_thenReturnEmptyList(){ + void updateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNoTimesAndAllAbsencesWithHomeOfficeAndVacation_thenReturnEmptyList() { Employee userUnderTest = createEmployeeForId("099-testUser", "test.user@gepardec.com", "2024-02-29"); when(employeeService.getAllActiveEmployees()) .thenReturn( - List.of( - createEmployeeForId("e02-externalUser", "external.user@gepardec.com", "2024-02-29"), - userUnderTest, - createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29") - ) + List.of( + createEmployeeForId("e02-externalUser", "external.user@gepardec.com", "2024-02-29"), + userUnderTest, + createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29") + ) ); @@ -138,17 +139,16 @@ void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNo } - @Test - void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNoTimesAndSomeAbsences_thenReturnEmptyList(){ + void updateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNoTimesAndSomeAbsences_thenReturnEmptyList() { Employee userUnderTest = createEmployeeForId("099-testUser", "test.user@gepardec.com", "2024-02-29"); when(employeeService.getAllActiveEmployees()) .thenReturn( - List.of( - createEmployeeForId("e02-externalUser", "external.user@gepardec.com", "2024-02-29"), - createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29"), - userUnderTest - ) + List.of( + createEmployeeForId("e02-externalUser", "external.user@gepardec.com", "2024-02-29"), + createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29"), + userUnderTest + ) ); @@ -172,15 +172,15 @@ void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeHasNo } @Test - void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeIsExternal_thenReturnEmptyList() { - Employee userUnderTest = createEmployeeForId("e02-externalUser", "external.user@gepardec.com", "2024-02-29"); + void updateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeIsExternal_thenReturnEmptyList() { + Employee userUnderTest = createEmployeeForId("e02-externalUser", "external.user@gepardec.com", "2024-02-29"); when(employeeService.getAllActiveEmployees()) .thenReturn( - List.of( - userUnderTest, - createEmployeeForId("099-testUser", "test.user@gepardec.com","2024-02-29"), - createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29") - ) + List.of( + userUnderTest, + createEmployeeForId("099-testUser", "test.user@gepardec.com", "2024-02-29"), + createEmployeeForId("100-testUser2", "test.user2@gepardec.com", "2024-02-29") + ) ); @@ -207,7 +207,7 @@ void testUpdateEmployeesWithoutTimeBookingsAndAbsentWholeMonth_whenEmployeeIsExt } //helpers - private Employee createEmployeeForId(final String id, final String email, final String releaseDate){ + private Employee createEmployeeForId(final String id, final String email, final String releaseDate) { return Employee.builder() .userId(id) .email(email) @@ -217,7 +217,7 @@ private Employee createEmployeeForId(final String id, final String email, final } - private static AbsenceTime createFehlzeitTypeForUser(final String userId, final String startDate, final String endDate, final String reason){ + private static AbsenceTime createFehlzeitTypeForUser(final String userId, final String startDate, final String endDate, final String reason) { return new AbsenceTime( userId, LocalDate.parse(startDate), @@ -227,7 +227,7 @@ private static AbsenceTime createFehlzeitTypeForUser(final String userId, final ); } - private static StepEntry createStepEntry(){ + private static StepEntry createStepEntry() { StepEntry entry = new StepEntry(); entry.setState(EmployeeState.OPEN); return entry; diff --git a/src/test/java/com/gepardec/mega/rest/UserResourceTest.java b/src/test/java/com/gepardec/mega/rest/UserResourceTest.java index 4c3e2ef4c..8745c8eca 100644 --- a/src/test/java/com/gepardec/mega/rest/UserResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/UserResourceTest.java @@ -9,8 +9,8 @@ import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.mockito.InjectSpy; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import org.apache.http.HttpStatus; import org.junit.jupiter.api.Test; @@ -22,7 +22,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) class UserResourceTest { @@ -35,7 +35,7 @@ class UserResourceTest { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void get_whenUserNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { final User user = createUserForRole(Role.EMPLOYEE); when(userContext.getUser()).thenReturn(user); diff --git a/src/test/java/com/gepardec/mega/rest/WorkerResourceTest.java b/src/test/java/com/gepardec/mega/rest/WorkerResourceTest.java index 9edfa2add..b6863a3f8 100644 --- a/src/test/java/com/gepardec/mega/rest/WorkerResourceTest.java +++ b/src/test/java/com/gepardec/mega/rest/WorkerResourceTest.java @@ -45,8 +45,8 @@ import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import io.restassured.http.ContentType; import jakarta.inject.Inject; import org.apache.commons.lang3.tuple.Pair; @@ -69,7 +69,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) public class WorkerResourceTest { @@ -140,7 +140,7 @@ void monthlyReport_whenDELETE_thenReturnsHttpStatusMETHOD_NOT_ALLOWED() { @Test @TestSecurity - @JwtSecurity + @OidcSecurity void monthlyReport_whenUserNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { final User user = createUserForRole(Role.EMPLOYEE); when(userContext.getUser()).thenReturn(user); @@ -253,7 +253,7 @@ void monthlyReport_withYearMonth_whenDELETE_thenReturnsHttpStatusMETHOD_NOT_ALLO @Test @TestSecurity - @JwtSecurity + @OidcSecurity void monthlyReport_withYearMonth_whenUserNotLogged_thenReturnsHttpStatusUNAUTHORIZED() { final User user = createUserForRole(Role.EMPLOYEE); when(userContext.getUser()).thenReturn(user); diff --git a/src/test/java/com/gepardec/mega/service/impl/init/StepEntrySyncServiceImplTest.java b/src/test/java/com/gepardec/mega/service/impl/init/StepEntrySyncServiceImplTest.java index e2000946d..f063d124d 100644 --- a/src/test/java/com/gepardec/mega/service/impl/init/StepEntrySyncServiceImplTest.java +++ b/src/test/java/com/gepardec/mega/service/impl/init/StepEntrySyncServiceImplTest.java @@ -25,6 +25,7 @@ import java.util.Set; import java.util.stream.Collectors; +import static com.gepardec.mega.domain.utils.DateUtils.getFirstDayOfCurrentMonth; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; @@ -152,7 +153,7 @@ void whenAllDataIsProvided_thenInsertDefinedNumberOfItems() { // default setup // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then verify(stepEntryService, times(22)).addStepEntry(Mockito.any()); @@ -165,7 +166,7 @@ void whenNoProjectsProvided_thenDoNotInsertProjectLead() { when(projectService.getProjectsForMonthYear(Mockito.any(), Mockito.anyList())).thenReturn(List.of()); // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -202,7 +203,7 @@ void whenProjectLeadIsInactive_thenDoNotInsertProjectLead() { .build())); // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -227,7 +228,7 @@ void whenDefaultDataIsProvided_thenInsertProjectLead() { // default setup when(notificationConfig.getOmMailAddresses()).thenReturn(List.of()); // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -252,7 +253,7 @@ void whenOfficeManagmentIsInactive_thenDoNotInsertOfficeManagment() { when(notificationConfig.getOmMailAddresses()).thenReturn(List.of("some.user@gepardec.com")); // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -273,7 +274,7 @@ void whenNoOfficeManagmentIsProvided_thenDoNotInsertOfficeManagment() { when(notificationConfig.getOmMailAddresses()).thenReturn(List.of()); // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -294,7 +295,7 @@ void whenDefaultDataIsProvided_thenInsertOfficeManagment() { // default setup // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -315,7 +316,7 @@ void whenNoActiveUsers_thenDoNotInsertEmployees() { when(userService.findActiveUsers()).thenReturn(List.of()); // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then verify(stepEntryService, never()).addStepEntry(Mockito.any()); @@ -327,7 +328,7 @@ void whenDefaultDataIsProvided_thenInsertEmployees() { // default setup // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -356,7 +357,7 @@ void whenNoStepsProvided_thenDoNotInsertSteps() { when(stepService.getSteps()).thenReturn(List.of()); // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then verify(stepEntryService, never()).addStepEntry(Mockito.any()); @@ -368,7 +369,7 @@ void whenDefaultDataIsProvided_thenInsertSteps() { // default setup // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); @@ -391,7 +392,7 @@ void whenDefaultDataIsProvided_thenInsertSteps2() { // default setup // When - stepEntrySyncService.generateStepEntriesFromEndpoint(); + stepEntrySyncService.generateStepEntries(getFirstDayOfCurrentMonth()); // Then final ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(StepEntry.class); diff --git a/src/test/java/com/gepardec/mega/service/impl/monthlyreport/MonthlyReportServiceImplTest.java b/src/test/java/com/gepardec/mega/service/impl/monthlyreport/MonthlyReportServiceImplTest.java index 3ff7f7f64..b22b4b44d 100644 --- a/src/test/java/com/gepardec/mega/service/impl/monthlyreport/MonthlyReportServiceImplTest.java +++ b/src/test/java/com/gepardec/mega/service/impl/monthlyreport/MonthlyReportServiceImplTest.java @@ -23,8 +23,8 @@ import io.quarkus.test.InjectMock; import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.security.TestSecurity; -import io.quarkus.test.security.jwt.Claim; -import io.quarkus.test.security.jwt.JwtSecurity; +import io.quarkus.test.security.oidc.Claim; +import io.quarkus.test.security.oidc.OidcSecurity; import jakarta.inject.Inject; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -50,7 +50,7 @@ @QuarkusTest @TestSecurity(user = "test") -@JwtSecurity(claims = { +@OidcSecurity(claims = { @Claim(key = "email", value = "test@gepardec.com") }) class MonthlyReportServiceImplTest { diff --git a/src/test/java/com/gepardec/mega/service/impl/stepentry/PrematureEmployeeCheckSyncServiceTest.java b/src/test/java/com/gepardec/mega/service/impl/stepentry/PrematureEmployeeCheckSyncServiceTest.java index e984fb215..2bbd3448a 100644 --- a/src/test/java/com/gepardec/mega/service/impl/stepentry/PrematureEmployeeCheckSyncServiceTest.java +++ b/src/test/java/com/gepardec/mega/service/impl/stepentry/PrematureEmployeeCheckSyncServiceTest.java @@ -15,7 +15,6 @@ import org.junit.jupiter.api.Test; import java.time.LocalDate; -import java.time.YearMonth; import java.util.List; import java.util.Optional; @@ -52,7 +51,7 @@ void syncPrematureEmployeeChecksWithStepEntries_matchingStepEntryAndPrematureEmp // When boolean updatedAllEntries = prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries( - YearMonth.of(testDate.getYear(), testDate.getMonth()) + testDate.withDayOfMonth(1) ); // Then @@ -77,7 +76,7 @@ void syncPrematureEmployeeChecksWithStepEntries_multipleMatchingStepEntryAndPrem // When boolean updatedAllEntries = prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries( - YearMonth.of(testDate.getYear(), testDate.getMonth()) + testDate.withDayOfMonth(1) ); // Then @@ -97,7 +96,7 @@ void syncPrematureEmployeeChecksWithStepEntries_nonMatchingStepEntryAndPremature // When boolean updatedAllEntries = prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries( - YearMonth.of(testDate.getYear(), testDate.getMonth()) + testDate.withDayOfMonth(1) ); // Then @@ -124,7 +123,7 @@ void syncPrematureEmployeeChecksWithStepEntries_mishedMatchingStepEntryAndPremat // When boolean updatedAllEntries = prematureEmployeeCheckSyncService.syncPrematureEmployeeChecksWithStepEntries( - YearMonth.of(testDate.getYear(), testDate.getMonth()) + testDate.withDayOfMonth(1) ); // Then