Refactor hawkbit core and security (#2833)
* Refactor hawkbit core and security * improve access to the base core features - static * thus easiear access * and less boilerplate passing of instances Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com> * Refactor context classes * make JSON context serialization default * AccessContext * Split hawkbit-security-core to other modules and remove it --------- Signed-off-by: Avgustin Marinov <Avgustin.Marinov@bosch.com>
This commit is contained in:
@@ -34,7 +34,7 @@
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.hawkbit</groupId>
|
||||
<artifactId>hawkbit-security-core</artifactId>
|
||||
<artifactId>hawkbit-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
|
||||
@@ -17,8 +17,8 @@ import jakarta.validation.constraints.NotNull;
|
||||
import org.eclipse.hawkbit.artifact.exception.ArtifactUploadFailedException;
|
||||
import org.eclipse.hawkbit.artifact.model.ArtifactStream;
|
||||
import org.eclipse.hawkbit.artifact.model.StoredArtifactInfo;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidMd5HashException;
|
||||
|
||||
@@ -15,11 +15,10 @@ import java.util.Optional;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.AutoConfirmationStatus;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
/**
|
||||
@@ -40,7 +39,7 @@ public interface ConfirmationManagement extends PermissionSupport {
|
||||
* Activate auto confirmation for a given controller ID. In case auto confirmation is active already, this method will fail with an exception.
|
||||
*
|
||||
* @param controllerId to activate the feature for
|
||||
* @param initiator who initiated this operation. If 'null' we will take the current user from {@link TenantAware#getCurrentUsername()}
|
||||
* @param initiator who initiated this operation.
|
||||
* @param remark optional field to set a remark
|
||||
* @return the persisted {@link AutoConfirmationStatus}
|
||||
*/
|
||||
|
||||
@@ -20,7 +20,7 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
|
||||
@@ -9,13 +9,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.CREATE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.DISTRIBUTION_SET;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.HANDLE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.UPDATE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_UPDATE_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpPermission.CREATE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.auth.SpPermission.DISTRIBUTION_SET;
|
||||
import static org.eclipse.hawkbit.auth.SpPermission.UPDATE_ROLLOUT;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_UPDATE_REPOSITORY;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -27,8 +25,8 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
@@ -51,6 +49,7 @@ import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
/**
|
||||
@@ -70,23 +69,6 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* Assigns {@link DistributionSet}s to {@link Target}s according to the {@link DeploymentRequest}.
|
||||
*
|
||||
* @param deploymentRequests information about all target-ds-assignments that shall be made
|
||||
* @return the list of assignment results
|
||||
* @throws IncompleteDistributionSetException if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
* defined by the {@link DistributionSetType}.
|
||||
* @throws EntityNotFoundException if either provided {@link DistributionSet} or {@link Target}s do not exist
|
||||
* @throws AssignmentQuotaExceededException if the maximum number of targets the distribution set can be
|
||||
* assigned to at once is exceeded
|
||||
* @throws MultiAssignmentIsNotEnabledException if the request results in multiple assignments to the same
|
||||
* target and multi-assignment is disabled
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(@Valid @NotEmpty List<DeploymentRequest> deploymentRequests);
|
||||
|
||||
/**
|
||||
* Assigns {@link DistributionSet}s to {@link Target}s according to the {@link DeploymentRequest}.
|
||||
*
|
||||
* @param initiatedBy the username of the user who initiated the assignment
|
||||
* @param deploymentRequests information about all target-ds-assignments that shall be made
|
||||
* @param actionMessage an optional message for the action status
|
||||
* @return the list of assignment results
|
||||
* @throws IncompleteDistributionSetException if mandatory {@link SoftwareModuleType} are not assigned as
|
||||
@@ -99,7 +81,7 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
String initiatedBy, @Valid @NotEmpty List<DeploymentRequest> deploymentRequests, String actionMessage);
|
||||
@Valid @NotEmpty List<DeploymentRequest> deploymentRequests, @Nullable String actionMessage);
|
||||
|
||||
/**
|
||||
* Registers "offline" assignments. "offline" assignment means adding a completed action for a {@link DistributionSet} to a {@link Target}.
|
||||
@@ -123,9 +105,6 @@ public interface DeploymentManagement extends PermissionSupport {
|
||||
* @throws MultiAssignmentIsNotEnabledException if the request results in multiple assignments to the same
|
||||
* target and multi-assignment is disabled
|
||||
*/
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(String initiatedBy, Collection<Entry<String, Long>> assignments);
|
||||
|
||||
@PreAuthorize(HAS_UPDATE_TARGET_AND_READ_DISTRIBUTION_SET)
|
||||
List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(Collection<Entry<String, Long>> assignments);
|
||||
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
@@ -9,13 +9,12 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_UPDATE_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_UPDATE_REPOSITORY;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
@@ -27,7 +26,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
|
||||
@@ -16,7 +16,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
|
||||
@@ -21,8 +21,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
|
||||
@@ -15,7 +15,7 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
|
||||
@@ -19,7 +19,7 @@ import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.NotEmpty;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout.RolloutStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
|
||||
@@ -9,12 +9,10 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
|
||||
@@ -9,8 +9,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
|
||||
@@ -28,8 +28,8 @@ import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityReadOnlyException;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.security.crypto.codec.Hex;
|
||||
import org.springframework.security.crypto.keygen.BytesKeyGenerator;
|
||||
import org.springframework.security.crypto.keygen.KeyGenerators;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link SecurityTokenGenerator} and make it
|
||||
* accessible to beans which are not managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@SuppressWarnings("java:S6548") // java:S6548 - singleton holder ensures static access to spring resources in some places
|
||||
public final class SecurityTokenGenerator {
|
||||
|
||||
private static final int TOKEN_LENGTH = 16;
|
||||
private static final BytesKeyGenerator SECURE_RANDOM = KeyGenerators.secureRandom(TOKEN_LENGTH);
|
||||
|
||||
/**
|
||||
* Generates a random secure token of {@link #TOKEN_LENGTH} bytes length as hexadecimal string.
|
||||
*
|
||||
* @return a new generated random alphanumeric string.
|
||||
*/
|
||||
public static String generateToken() {
|
||||
return new String(Hex.encode(SECURE_RANDOM.generateKey()));
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2025 Contributors to the Eclipse Foundation
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds the {@link SecurityTokenGenerator} and make it
|
||||
* accessible to beans which are not managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@SuppressWarnings("java:S6548") // java:S6548 - singleton holder ensures static access to spring resources in some places
|
||||
public final class SecurityTokenGeneratorHolder {
|
||||
|
||||
private static final SecurityTokenGeneratorHolder SINGLETON = new SecurityTokenGeneratorHolder();
|
||||
|
||||
private SecurityTokenGenerator securityTokenGenerator;
|
||||
|
||||
/**
|
||||
* @return a singleton instance of the security token generator holder.
|
||||
*/
|
||||
public static SecurityTokenGeneratorHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Autowired // spring setter injection
|
||||
public void setSecurityTokenGenerator(final SecurityTokenGenerator securityTokenGenerator) {
|
||||
this.securityTokenGenerator = securityTokenGenerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* delegates to {@link SecurityTokenGenerator#generateToken()}.
|
||||
*
|
||||
* @return the result {@link SecurityTokenGenerator#generateToken()}
|
||||
*/
|
||||
public String generateToken() {
|
||||
return securityTokenGenerator.generateToken();
|
||||
}
|
||||
}
|
||||
@@ -20,8 +20,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
|
||||
@@ -20,8 +20,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.Type;
|
||||
|
||||
@@ -13,14 +13,12 @@ import java.util.function.Consumer;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.eclipse.hawkbit.repository.model.report.SystemUsageReport;
|
||||
import org.eclipse.hawkbit.repository.model.report.SystemUsageReportWithTenants;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
@@ -30,14 +28,6 @@ import org.springframework.security.access.prepost.PreAuthorize;
|
||||
*/
|
||||
public interface SystemManagement {
|
||||
|
||||
/**
|
||||
* Deletes all data related to a given tenant.
|
||||
*
|
||||
* @param tenant to delete
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||
void deleteTenant(@NotNull String tenant);
|
||||
|
||||
/**
|
||||
* @param pageable for paging information
|
||||
* @return list of all tenant names in the system.
|
||||
@@ -47,7 +37,7 @@ public interface SystemManagement {
|
||||
|
||||
/**
|
||||
* Runs consumer for each tenant as
|
||||
* {@link TenantAware#runAsTenant(String, java.util.concurrent.Callable)}
|
||||
* {@link AccessContext#asSystemAsTenant(String, java.util.concurrent.Callable)}
|
||||
* silently (i.e. exceptions will be logged but operations will continue for further tenants).
|
||||
*
|
||||
* @param consumer to run as tenant
|
||||
@@ -56,37 +46,15 @@ public interface SystemManagement {
|
||||
void forEachTenant(Consumer<String> consumer);
|
||||
|
||||
/**
|
||||
* Calculated system usage statistics, both overall for the entire system and per tenant;
|
||||
*
|
||||
* @return SystemUsageReport of the current system
|
||||
* @return {@link TenantMetaData} of {@link AccessContext#tenant()}
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||
SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants();
|
||||
|
||||
/**
|
||||
* Calculated overall system usage statistics
|
||||
*
|
||||
* @return SystemUsageReport of the current system
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||
SystemUsageReport getSystemUsageStatistics();
|
||||
|
||||
/**
|
||||
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()}
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('" + SpPermission.READ_DISTRIBUTION_SET + "')" + " or "
|
||||
+ "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or "
|
||||
+ "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or "
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
@PreAuthorize("hasAuthority('" + SpPermission.READ_DISTRIBUTION_SET + "')" + " or " + "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or " + "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or " + SpringEvalExpressions.IS_CONTROLLER)
|
||||
TenantMetaData getTenantMetadata();
|
||||
|
||||
/**
|
||||
* @return {@link TenantMetaData} of {@link TenantAware#getCurrentTenant()} without details ({@link TenantMetaData#getDefaultDsType()})
|
||||
* @return {@link TenantMetaData} of {@link AccessContext#tenant()} without details ({@link TenantMetaData#getDefaultDsType()})
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('" + SpPermission.READ_DISTRIBUTION_SET + "')" + " or "
|
||||
+ "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or "
|
||||
+ "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or "
|
||||
+ SpringEvalExpressions.IS_CONTROLLER)
|
||||
@PreAuthorize("hasAuthority('" + SpPermission.READ_DISTRIBUTION_SET + "')" + " or " + "hasAuthority('READ_" + SpPermission.TARGET + "')" + " or " + "hasAuthority('READ_" + SpPermission.TENANT_CONFIGURATION + "')" + " or " + SpringEvalExpressions.IS_CONTROLLER)
|
||||
TenantMetaData getTenantMetadataWithoutDetails();
|
||||
|
||||
/**
|
||||
@@ -113,4 +81,12 @@ public interface SystemManagement {
|
||||
*/
|
||||
@PreAuthorize("hasAuthority('UPDATE_" + SpPermission.TENANT_CONFIGURATION + "')")
|
||||
TenantMetaData updateTenantMetadata(long defaultDsType);
|
||||
|
||||
/**
|
||||
* Deletes all data related to a given tenant.
|
||||
*
|
||||
* @param tenant to delete
|
||||
*/
|
||||
@PreAuthorize(SpringEvalExpressions.HAS_AUTH_SYSTEM_ADMIN)
|
||||
void deleteTenant(@NotNull String tenant);
|
||||
}
|
||||
@@ -22,8 +22,8 @@ import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.Accessors;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_DELETE_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_UPDATE_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_DELETE_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_UPDATE_REPOSITORY;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
@@ -29,8 +29,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
@@ -466,7 +466,7 @@ public interface TargetManagement<T extends Target>
|
||||
: controllerId
|
||||
: builder.name;
|
||||
securityToken = ObjectUtils.isEmpty(builder.securityToken)
|
||||
? SecurityTokenGeneratorHolder.getInstance().generateToken()
|
||||
? SecurityTokenGenerator.generateToken()
|
||||
: builder.securityToken;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
import org.eclipse.hawkbit.repository.model.Tag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
import static org.eclipse.hawkbit.auth.SpringEvalExpressions.HAS_READ_REPOSITORY;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -24,8 +24,8 @@ import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.ToString;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.TargetTypeKeyOrNameRequiredException;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.NamedEntity;
|
||||
|
||||
@@ -13,8 +13,8 @@ import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
|
||||
@@ -9,8 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.SpRole;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.repository.model.report.TenantUsage;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
|
||||
|
||||
@@ -9,9 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Enumerates the supported update modes. Each mode represents an attribute update strategy.
|
||||
*
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||
import com.fasterxml.jackson.annotation.JsonTypeInfo;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
|
||||
|
||||
@@ -17,7 +17,6 @@ import lombok.NoArgsConstructor;
|
||||
import lombok.ToString;
|
||||
import org.eclipse.hawkbit.repository.event.entity.EntityDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds {@link SystemSecurityContext} service and makes it accessible to beans which are not
|
||||
* managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@SuppressWarnings("java:S6548") // java:S6548 - singleton holder ensures static access to spring resources in some places
|
||||
public final class SystemSecurityContextHolder {
|
||||
|
||||
private static final SystemSecurityContextHolder SINGLETON = new SystemSecurityContextHolder();
|
||||
|
||||
@Getter
|
||||
private SystemSecurityContext systemSecurityContext;
|
||||
|
||||
/**
|
||||
* @return the singleton {@link SystemSecurityContextHolder} instance
|
||||
*/
|
||||
public static SystemSecurityContextHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Autowired // spring setter injection
|
||||
public void setSystemSecurityContext(final SystemSecurityContext systemSecurityContext) {
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Copyright (c) 2019 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.helper;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.USER_CONFIRMATION_FLOW_ENABLED;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
|
||||
/**
|
||||
* A collection of static helper methods for the tenant configuration
|
||||
*/
|
||||
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
|
||||
public final class TenantConfigHelper {
|
||||
|
||||
private static TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
// method to be initialized by the TenantConfigurationManagement or TenantConfigurationManagement creator
|
||||
// it will be accessed directly and used so shall be fully initialized instance, i.e. a bean in order to onore things
|
||||
// like @PreAuthorize, @Transactional etc.
|
||||
public static void setTenantConfigurationManagement(final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
TenantConfigHelper.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
}
|
||||
|
||||
public static TenantConfigurationManagement getTenantConfigurationManagement() {
|
||||
return Objects.requireNonNull(tenantConfigurationManagement, "TenantConfigurationManagement has not been initialized");
|
||||
}
|
||||
|
||||
public static <T extends Serializable> T getAsSystem(final String key, final Class<T> valueType) {
|
||||
return asSystem(() -> getTenantConfigurationManagement().getConfigurationValue(key, valueType).getValue());
|
||||
}
|
||||
|
||||
public static boolean isMultiAssignmentsEnabled() {
|
||||
return getAsSystem(MULTI_ASSIGNMENTS_ENABLED, Boolean.class);
|
||||
}
|
||||
|
||||
public static boolean isUserConfirmationFlowEnabled() {
|
||||
return getAsSystem(USER_CONFIRMATION_FLOW_ENABLED, Boolean.class);
|
||||
}
|
||||
|
||||
public static Function<Target, PollStatus> pollStatusResolver() {
|
||||
return getTenantConfigurationManagement().pollStatusResolver();
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds {@link TenantConfigurationManagement} service and makes it accessible to beans which are
|
||||
* not managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@SuppressWarnings("java:S6548") // java:S6548 - singleton holder ensures static access to spring resources in some places
|
||||
public final class TenantConfigurationManagementHolder {
|
||||
|
||||
private static final TenantConfigurationManagementHolder SINGLETON = new TenantConfigurationManagementHolder();
|
||||
|
||||
@Getter
|
||||
private TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
/**
|
||||
* @return the singleton {@link TenantConfigurationManagementHolder} instance
|
||||
*/
|
||||
public static TenantConfigurationManagementHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Autowired // spring setter injection
|
||||
public void setTenantConfigurationManagement(final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ package org.eclipse.hawkbit.repository.model;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.Identifiable;
|
||||
|
||||
/**
|
||||
@@ -61,7 +62,7 @@ public interface Target extends NamedEntity, Identifiable<Long> {
|
||||
|
||||
/**
|
||||
* @return the securityToken if the current security context contains the necessary permission
|
||||
* {@link org.eclipse.hawkbit.im.authentication.SpPermission#READ_TARGET_SECURITY_TOKEN}
|
||||
* {@link SpPermission#READ_TARGET_SECURITY_TOKEN}
|
||||
* or the current context is executed as system code, otherwise {@code null}.
|
||||
*/
|
||||
String getSecurityToken();
|
||||
|
||||
@@ -14,8 +14,7 @@ import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationPrope
|
||||
|
||||
import java.time.Duration;
|
||||
|
||||
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
|
||||
import org.springframework.util.PropertyPlaceholderHelper;
|
||||
@@ -81,8 +80,6 @@ public class VirtualPropertyResolver {
|
||||
}
|
||||
|
||||
private static String getRawStringForKey(final String key) {
|
||||
return SystemSecurityContextHolder.getInstance().getSystemSecurityContext().runAsSystem(
|
||||
() -> TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
|
||||
.getConfigurationValue(key, String.class).getValue());
|
||||
return TenantConfigHelper.getAsSystem(key, String.class);
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public class TenantConfigurationProperties {
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant specific configurations which can be configured for each tenant separately by means of override of the system defaults.
|
||||
* AccessContext specific configurations which can be configured for each tenant separately by means of override of the system defaults.
|
||||
*/
|
||||
@Data
|
||||
@ToString
|
||||
@@ -127,7 +127,7 @@ public class TenantConfigurationProperties {
|
||||
/**
|
||||
* Switch to enable/disable the user-confirmation flow
|
||||
*/
|
||||
public static final String USER_CONFIRMATION_ENABLED = "user.confirmation.flow.enabled";
|
||||
public static final String USER_CONFIRMATION_FLOW_ENABLED = "user.confirmation.flow.enabled";
|
||||
/**
|
||||
* Switch to enable/disable the implicit locking
|
||||
*/
|
||||
|
||||
@@ -1,76 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2019 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.utils;
|
||||
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.USER_CONFIRMATION_ENABLED;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
|
||||
/**
|
||||
* A collection of static helper methods for the tenant configuration
|
||||
*/
|
||||
public final class TenantConfigHelper {
|
||||
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private TenantConfigHelper(
|
||||
final SystemSecurityContext systemSecurityContext,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting the context of the tenant.
|
||||
*
|
||||
* @param systemSecurityContext Security context used to get the tenant and for execution
|
||||
* @param tenantConfigurationManagement to get the value from
|
||||
* @return is active
|
||||
*/
|
||||
public static TenantConfigHelper usingContext(
|
||||
final SystemSecurityContext systemSecurityContext,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
return new TenantConfigHelper(systemSecurityContext, tenantConfigurationManagement);
|
||||
}
|
||||
|
||||
public <T extends Serializable> T getConfigValue(final String key, final Class<T> valueType) {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* Is multi-assignments enabled for the current tenant
|
||||
*
|
||||
* @return is active
|
||||
*/
|
||||
public boolean isMultiAssignmentsEnabled() {
|
||||
return getConfigValue(MULTI_ASSIGNMENTS_ENABLED, Boolean.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Is confirmation flow enabled for the current tenant
|
||||
*
|
||||
* @return is enabled
|
||||
*/
|
||||
public boolean isConfirmationFlowEnabled() {
|
||||
return getConfigValue(USER_CONFIRMATION_ENABLED, Boolean.class);
|
||||
}
|
||||
|
||||
public Function<Target, PollStatus> pollStatusResolver() {
|
||||
return tenantConfigurationManagement.pollStatusResolver();
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.event;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import io.protostuff.ProtostuffIOUtil;
|
||||
import io.protostuff.Schema;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -19,7 +22,6 @@ import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RemoteTenantAwareEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.AbstractServiceRemoteEvent;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
@@ -47,10 +49,9 @@ public class EventPublisherConfiguration {
|
||||
*/
|
||||
@Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
|
||||
ApplicationEventMulticaster applicationEventMulticaster(
|
||||
@Qualifier("asyncExecutor") final Executor executor,
|
||||
final SystemSecurityContext systemSecurityContext, final ApplicationEventFilter applicationEventFilter) {
|
||||
@Qualifier("asyncExecutor") final Executor executor, final ApplicationEventFilter applicationEventFilter) {
|
||||
final SimpleApplicationEventMulticaster simpleApplicationEventMulticaster =
|
||||
new TenantAwareApplicationEventPublisher(systemSecurityContext, applicationEventFilter);
|
||||
new TenantAwareApplicationEventPublisher(applicationEventFilter);
|
||||
simpleApplicationEventMulticaster.setTaskExecutor(executor);
|
||||
return simpleApplicationEventMulticaster;
|
||||
}
|
||||
@@ -76,17 +77,14 @@ public class EventPublisherConfiguration {
|
||||
|
||||
private static class TenantAwareApplicationEventPublisher extends SimpleApplicationEventMulticaster {
|
||||
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final ApplicationEventFilter applicationEventFilter;
|
||||
|
||||
protected TenantAwareApplicationEventPublisher(
|
||||
final SystemSecurityContext systemSecurityContext, final ApplicationEventFilter applicationEventFilter) {
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
protected TenantAwareApplicationEventPublisher(final ApplicationEventFilter applicationEventFilter) {
|
||||
this.applicationEventFilter = applicationEventFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Was overridden that not every event has to run within an own tenantAware.
|
||||
* Was overridden that not every event has to run within an own tenant.
|
||||
*/
|
||||
@Override
|
||||
public void multicastEvent(final ApplicationEvent event, final ResolvableType eventType) {
|
||||
@@ -95,19 +93,13 @@ public class EventPublisherConfiguration {
|
||||
}
|
||||
|
||||
if (event instanceof final RemoteTenantAwareEvent remoteEvent) {
|
||||
systemSecurityContext.runAsSystemAsTenant(() -> {
|
||||
super.multicastEvent(event, eventType);
|
||||
return null;
|
||||
}, remoteEvent.getTenant());
|
||||
asSystemAsTenant(remoteEvent.getTenant(), () -> super.multicastEvent(event, eventType));
|
||||
return;
|
||||
}
|
||||
|
||||
if (event instanceof final AbstractServiceRemoteEvent<?> serviceRemoteEvent
|
||||
&& serviceRemoteEvent.getRemoteEvent() instanceof RemoteTenantAwareEvent tenantAwareEvent) {
|
||||
systemSecurityContext.runAsSystemAsTenant(() -> {
|
||||
super.multicastEvent(event, eventType);
|
||||
return null;
|
||||
}, tenantAwareEvent.getTenant());
|
||||
asSystemAsTenant(tenantAwareEvent.getTenant(), () -> super.multicastEvent(event, eventType));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,15 +26,6 @@ import org.eclipse.hawkbit.repository.event.remote.DistributionSetTypeDeletedEve
|
||||
import org.eclipse.hawkbit.repository.event.remote.DownloadProgressEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionAssignEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.MultiActionCancelEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionUpdatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionAssignServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionCancelServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAssignDistributionSetServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAttributesRequestedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutGroupDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.RolloutStoppedEvent;
|
||||
@@ -74,6 +65,15 @@ import org.eclipse.hawkbit.repository.event.remote.entity.TargetTypeUpdatedEvent
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TenantConfigurationUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.ActionUpdatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.CancelTargetAssignmentServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionAssignServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.MultiActionCancelServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAssignDistributionSetServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetAttributesRequestedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetCreatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetUpdatedServiceEvent;
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,8 +12,8 @@ package org.eclipse.hawkbit.repository;
|
||||
import java.util.Optional;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.im.authentication.Hierarchy;
|
||||
import org.eclipse.hawkbit.im.authentication.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.auth.Hierarchy;
|
||||
import org.eclipse.hawkbit.auth.SpringEvalExpressions;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -47,8 +47,7 @@ public class RepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@SuppressWarnings("java:S3358")
|
||||
// java:S3358 better readable this way
|
||||
@SuppressWarnings("java:S3358") // java:S3358 better readable this way
|
||||
RoleHierarchy roleHierarchy(
|
||||
// if configured replaces the hierarchy completely
|
||||
@Value("${hawkbit.hierarchy:}") final String hierarchy,
|
||||
@@ -62,6 +61,9 @@ public class RepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
PermissionEvaluator permissionEvaluator(final RoleHierarchy roleHierarchy) {
|
||||
// sets up global access to the role hierarchy
|
||||
Hierarchy.setRoleHierarchy(roleHierarchy);
|
||||
// and returns a custom permission evaluator
|
||||
return new DenyAllPermissionEvaluator() {
|
||||
|
||||
@Override
|
||||
@@ -72,7 +74,8 @@ public class RepositoryConfiguration {
|
||||
.replace(SpringEvalExpressions.PERMISSION_GROUP_PLACEHOLDER, permissionSupport.permissionGroup());
|
||||
|
||||
// do permissions check
|
||||
final boolean hasPermission = roleHierarchy.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
||||
final boolean hasPermission = Hierarchy.getRoleHierarchy()
|
||||
.getReachableGrantedAuthorities(authentication.getAuthorities()).stream()
|
||||
.map(GrantedAuthority::getAuthority)
|
||||
.anyMatch(authority -> authority.equals(neededPermission));
|
||||
if (!hasPermission) {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -25,7 +27,6 @@ import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.context.event.EventListener;
|
||||
|
||||
@@ -39,19 +40,13 @@ public class RolloutStatusCache {
|
||||
|
||||
private static final TenantAwareCacheManager CACHE_MANAGER = TenantAwareCacheManager.getInstance();
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
public RolloutStatusCache(final TenantAware tenantAware) {
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves cached list of {@link TotalTargetCountActionStatus} of {@link Rollout}s.
|
||||
*
|
||||
* @param rollouts rolloutIds to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public Map<Long, List<TotalTargetCountActionStatus>> getRolloutStatus(final List<Long> rollouts) {
|
||||
public static Map<Long, List<TotalTargetCountActionStatus>> getRolloutStatus(final List<Long> rollouts) {
|
||||
return retrieveFromCache(rollouts, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -61,7 +56,7 @@ public class RolloutStatusCache {
|
||||
* @param rolloutId to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public List<TotalTargetCountActionStatus> getRolloutStatus(final Long rolloutId) {
|
||||
public static List<TotalTargetCountActionStatus> getRolloutStatus(final Long rolloutId) {
|
||||
return retrieveFromCache(rolloutId, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -71,7 +66,7 @@ public class RolloutStatusCache {
|
||||
* @param rolloutGroups rolloutGroupsIds to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public Map<Long, List<TotalTargetCountActionStatus>> getRolloutGroupStatus(final List<Long> rolloutGroups) {
|
||||
public static Map<Long, List<TotalTargetCountActionStatus>> getRolloutGroupStatus(final List<Long> rolloutGroups) {
|
||||
return retrieveFromCache(rolloutGroups, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@@ -81,7 +76,7 @@ public class RolloutStatusCache {
|
||||
* @param groupId to retrieve cache entries for
|
||||
* @return map of cached entries
|
||||
*/
|
||||
public List<TotalTargetCountActionStatus> getRolloutGroupStatus(final Long groupId) {
|
||||
public static List<TotalTargetCountActionStatus> getRolloutGroupStatus(final Long groupId) {
|
||||
return retrieveFromCache(groupId, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@@ -91,7 +86,7 @@ public class RolloutStatusCache {
|
||||
*
|
||||
* @param put map of cached entries
|
||||
*/
|
||||
public void putRolloutStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
public static void putRolloutStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
putIntoCache(put, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -101,7 +96,7 @@ public class RolloutStatusCache {
|
||||
* @param rolloutId the cache entries belong to
|
||||
* @param status list to cache
|
||||
*/
|
||||
public void putRolloutStatus(final Long rolloutId, final List<TotalTargetCountActionStatus> status) {
|
||||
public static void putRolloutStatus(final Long rolloutId, final List<TotalTargetCountActionStatus> status) {
|
||||
putIntoCache(rolloutId, status, getRolloutStatusCache());
|
||||
}
|
||||
|
||||
@@ -110,7 +105,7 @@ public class RolloutStatusCache {
|
||||
*
|
||||
* @param put map of cached entries
|
||||
*/
|
||||
public void putRolloutGroupStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
public static void putRolloutGroupStatus(final Map<Long, List<TotalTargetCountActionStatus>> put) {
|
||||
putIntoCache(put, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@@ -120,51 +115,51 @@ public class RolloutStatusCache {
|
||||
* @param groupId the cache entries belong to
|
||||
* @param status list to cache
|
||||
*/
|
||||
public void putRolloutGroupStatus(final Long groupId, final List<TotalTargetCountActionStatus> status) {
|
||||
public static void putRolloutGroupStatus(final Long groupId, final List<TotalTargetCountActionStatus> status) {
|
||||
putIntoCache(groupId, status, getGroupStatusCache());
|
||||
}
|
||||
|
||||
@EventListener(classes = AbstractActionEvent.class)
|
||||
public void invalidateCachedTotalTargetCountActionStatus(final AbstractActionEvent event) {
|
||||
if (event.getRolloutId() != null) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
cache.evict(event.getRolloutId());
|
||||
}
|
||||
|
||||
if (event.getRolloutGroupId() != null) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
cache.evict(event.getRolloutGroupId());
|
||||
}
|
||||
}
|
||||
|
||||
@EventListener(classes = RolloutDeletedEvent.class)
|
||||
public void invalidateCachedTotalTargetCountOnRolloutDelete(final RolloutDeletedEvent event) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
cache.evict(event.getEntityId());
|
||||
}
|
||||
|
||||
@EventListener(classes = RolloutGroupDeletedEvent.class)
|
||||
public void invalidateCachedTotalTargetCountOnRolloutGroupDelete(final RolloutGroupDeletedEvent event) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME));
|
||||
cache.evict(event.getEntityId());
|
||||
}
|
||||
|
||||
@EventListener(classes = RolloutStoppedEvent.class)
|
||||
public void invalidateCachedTotalTargetCountOnRolloutStopped(final RolloutStoppedEvent event) {
|
||||
final Cache cache = tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
final Cache cache = asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_RO_NAME));
|
||||
cache.evict(event.getRolloutId());
|
||||
event.getRolloutGroupIds().forEach(
|
||||
groupId -> tenantAware.runAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME)).evict(groupId));
|
||||
groupId -> asSystemAsTenant(event.getTenant(), () -> CACHE_MANAGER.getCache(CACHE_GR_NAME)).evict(groupId));
|
||||
}
|
||||
|
||||
private @NotNull Map<Long, List<TotalTargetCountActionStatus>> retrieveFromCache(final List<Long> ids, @NotNull final Cache cache) {
|
||||
private static @NotNull Map<Long, List<TotalTargetCountActionStatus>> retrieveFromCache(final List<Long> ids, @NotNull final Cache cache) {
|
||||
return ids.stream()
|
||||
.map(id -> cache.get(id, CachedTotalTargetCountActionStatus.class))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toMap(CachedTotalTargetCountActionStatus::id, CachedTotalTargetCountActionStatus::status));
|
||||
}
|
||||
|
||||
private List<TotalTargetCountActionStatus> retrieveFromCache(final Long id, @NotNull final Cache cache) {
|
||||
private static List<TotalTargetCountActionStatus> retrieveFromCache(final Long id, @NotNull final Cache cache) {
|
||||
final CachedTotalTargetCountActionStatus cacheItem = cache.get(id, CachedTotalTargetCountActionStatus.class);
|
||||
if (cacheItem == null) {
|
||||
return Collections.emptyList();
|
||||
@@ -172,20 +167,19 @@ public class RolloutStatusCache {
|
||||
return cacheItem.status();
|
||||
}
|
||||
|
||||
private void putIntoCache(final Long id, final List<TotalTargetCountActionStatus> status,
|
||||
@NotNull final Cache cache) {
|
||||
private static void putIntoCache(final Long id, final List<TotalTargetCountActionStatus> status, @NotNull final Cache cache) {
|
||||
cache.put(id, new CachedTotalTargetCountActionStatus(id, status));
|
||||
}
|
||||
|
||||
private void putIntoCache(final Map<Long, List<TotalTargetCountActionStatus>> put, @NotNull final Cache cache) {
|
||||
private static void putIntoCache(final Map<Long, List<TotalTargetCountActionStatus>> put, @NotNull final Cache cache) {
|
||||
put.forEach((k, v) -> cache.put(k, new CachedTotalTargetCountActionStatus(k, v)));
|
||||
}
|
||||
|
||||
private @NotNull Cache getRolloutStatusCache() {
|
||||
private static @NotNull Cache getRolloutStatusCache() {
|
||||
return Objects.requireNonNull(CACHE_MANAGER.getCache(CACHE_RO_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
|
||||
}
|
||||
|
||||
private @NotNull Cache getGroupStatusCache() {
|
||||
private static @NotNull Cache getGroupStatusCache() {
|
||||
return Objects.requireNonNull(CACHE_MANAGER.getCache(CACHE_GR_NAME), "Cache '" + CACHE_RO_NAME + "' is null!");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,11 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.event;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.eclipse.hawkbit.repository.event.remote.AbstractRemoteEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.ActionCreatedEvent;
|
||||
@@ -22,18 +27,12 @@ import org.eclipse.hawkbit.repository.event.remote.service.TargetDeletedServiceE
|
||||
import org.eclipse.hawkbit.repository.event.remote.service.TargetUpdatedServiceEvent;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
abstract class AbstractEventMessageConverterTest {
|
||||
|
||||
protected final MessageConverter messageConverter;
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2015 Bosch Software Innovations GmbH and others
|
||||
*
|
||||
* This program and the accompanying materials are made
|
||||
* available under the terms of the Eclipse Public License 2.0
|
||||
* which is available at https://www.eclipse.org/legal/epl-2.0/
|
||||
*
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.model.helper;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
||||
/**
|
||||
* A singleton bean which holds {@link TenantAware} service and makes it
|
||||
* accessible to beans which are not managed by spring, e.g. JPA entities.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||
@SuppressWarnings("java:S6548") // java:S6548 - singleton holder ensures static access to spring resources in some places
|
||||
public final class TenantAwareHolder {
|
||||
|
||||
private static final TenantAwareHolder SINGLETON = new TenantAwareHolder();
|
||||
|
||||
private TenantAware tenantAware;
|
||||
|
||||
/**
|
||||
* @return the singleton {@link TenantAwareHolder} instance
|
||||
*/
|
||||
public static TenantAwareHolder getInstance() {
|
||||
return SINGLETON;
|
||||
}
|
||||
|
||||
@Autowired // spring setter injection
|
||||
public void setTenantAware(final TenantAware tenantAware) {
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantAware} service
|
||||
*/
|
||||
public TenantAware getTenantAware() {
|
||||
return tenantAware;
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import jakarta.persistence.Query;
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.util.Map;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import lombok.Data;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
|
||||
@@ -45,7 +44,6 @@ public class JpaConfiguration extends JpaBaseConfiguration {
|
||||
private final Map<String, String> eclipselink = new HashMap<>();
|
||||
}
|
||||
|
||||
private final TenantAware.TenantResolver tenantResolver;
|
||||
// only for testing purposes ddl generation may be enabled
|
||||
private final Map<String, String> eclipselinkProperties;
|
||||
|
||||
@@ -53,10 +51,8 @@ public class JpaConfiguration extends JpaBaseConfiguration {
|
||||
protected JpaConfiguration(
|
||||
final DataSource dataSource, final JpaProperties properties,
|
||||
final ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider,
|
||||
final TenantAware.TenantResolver tenantResolver,
|
||||
final Properties eclipselinkProperties) {
|
||||
super(dataSource, properties, jtaTransactionManagerProvider);
|
||||
this.tenantResolver = tenantResolver;
|
||||
this.eclipselinkProperties = eclipselinkProperties.getEclipselink();
|
||||
}
|
||||
|
||||
@@ -67,7 +63,7 @@ public class JpaConfiguration extends JpaBaseConfiguration {
|
||||
@Override
|
||||
@Bean
|
||||
public PlatformTransactionManager transactionManager(final ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
|
||||
return new TransactionManager(tenantResolver);
|
||||
return new TransactionManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,9 +15,9 @@ import java.util.Objects;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.transaction.Transaction;
|
||||
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.EntityPropertyChangeListener;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.persistence.config.PersistenceUnitProperties;
|
||||
import org.eclipse.persistence.descriptors.ClassDescriptor;
|
||||
import org.eclipse.persistence.descriptors.DescriptorEventManager;
|
||||
@@ -29,7 +29,7 @@ import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.orm.jpa.JpaTransactionManager} that sets the {@link TenantAware#getCurrentTenant()} in the eclipselink session. This has
|
||||
* {@link org.springframework.orm.jpa.JpaTransactionManager} that sets the {@link AccessContext#tenant()} in the eclipselink session. This has
|
||||
* to be done in eclipselink after a {@link Transaction} has been started.
|
||||
* <p/>
|
||||
* The class also handles setting:
|
||||
@@ -45,8 +45,6 @@ class TransactionManager extends JpaTransactionManager {
|
||||
|
||||
private static final Class<?> JPA_TARGET;
|
||||
|
||||
private transient TenantAware.TenantResolver tenantResolver;
|
||||
|
||||
static {
|
||||
try {
|
||||
JPA_TARGET = Class.forName("org.eclipse.hawkbit.repository.jpa.model.JpaTarget");
|
||||
@@ -57,10 +55,6 @@ class TransactionManager extends JpaTransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
TransactionManager(final TenantAware.TenantResolver tenantResolver) {
|
||||
this.tenantResolver = tenantResolver;
|
||||
}
|
||||
|
||||
private static final EntityPropertyChangeListener ENTITY_PROPERTY_CHANGE_LISTENER = new EntityPropertyChangeListener();
|
||||
|
||||
@Override
|
||||
@@ -83,7 +77,7 @@ class TransactionManager extends JpaTransactionManager {
|
||||
}
|
||||
}
|
||||
|
||||
final String currentTenant = tenantResolver.resolveTenant();
|
||||
final String currentTenant = AccessContext.tenant();
|
||||
if (currentTenant == null) {
|
||||
cleanupTenant(em);
|
||||
} else {
|
||||
|
||||
@@ -22,8 +22,8 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.persistence.annotations.Multitenant;
|
||||
import org.eclipse.persistence.annotations.MultitenantType;
|
||||
@@ -50,7 +50,7 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
private String tenant;
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* AccessContext aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* multi-schema based data separation setup to have the same primary key for different entities of different tenants.
|
||||
*/
|
||||
@Override
|
||||
@@ -59,7 +59,7 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* AccessContext aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* multi-schema based data separation setup to have the same primary key for different entities of
|
||||
* different tenants.
|
||||
*/
|
||||
@@ -86,13 +86,13 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
*/
|
||||
@PrePersist
|
||||
void prePersist() {
|
||||
// before persisting the entity check the current ID of the tenant by using the TenantAware service
|
||||
final String currentTenant = TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant();
|
||||
// before persisting the entity check the current ID of the tenant by using the AccessContext
|
||||
final String currentTenant = AccessContext.tenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException(
|
||||
String.format(
|
||||
"Tenant %s does not exists, cannot create entity %s with id %d",
|
||||
TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant(), getClass(), getId()));
|
||||
"AccessContext %s does not exists, cannot create entity %s with id %d",
|
||||
AccessContext.tenant(), getClass(), getId()));
|
||||
}
|
||||
setTenant(currentTenant.toUpperCase());
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.Query;
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import lombok.Data;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.EntityPropertyChangeListener;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.JpaExceptionTranslator;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.hibernate.boot.Metadata;
|
||||
import org.hibernate.boot.spi.BootstrapContext;
|
||||
import org.hibernate.cfg.MultiTenancySettings;
|
||||
@@ -41,13 +41,10 @@ import org.springframework.orm.jpa.JpaTransactionManager;
|
||||
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaDialect;
|
||||
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
|
||||
import org.springframework.security.core.parameters.P;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.jta.JtaTransactionManager;
|
||||
import org.springframework.transaction.support.DefaultTransactionStatus;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* General Hibernate configuration for hawkBit's Repository.
|
||||
*/
|
||||
@@ -68,10 +65,9 @@ public class JpaConfiguration extends JpaBaseConfiguration {
|
||||
protected JpaConfiguration(
|
||||
final DataSource dataSource, final JpaProperties properties,
|
||||
final ObjectProvider<JtaTransactionManager> jtaTransactionManagerProvider,
|
||||
final TenantAware.TenantResolver tenantResolver,
|
||||
final Properties hibernateProperties) {
|
||||
super(dataSource, properties, jtaTransactionManagerProvider);
|
||||
tenantIdentifier = new TenantIdentifier(tenantResolver);
|
||||
tenantIdentifier = new TenantIdentifier();
|
||||
this.hibernateProperties = hibernateProperties.getHibernate();
|
||||
}
|
||||
|
||||
|
||||
@@ -11,26 +11,20 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.hibernate.context.spi.CurrentTenantIdentifierResolver;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer;
|
||||
|
||||
/**
|
||||
* {@link CurrentTenantIdentifierResolver} and {@link HibernatePropertiesCustomizer} that resolves the
|
||||
* {@link TenantAware#getCurrentTenant()} for hibernate.
|
||||
* {@link AccessContext#tenant()} for hibernate.
|
||||
*/
|
||||
class TenantIdentifier implements CurrentTenantIdentifierResolver<String> {
|
||||
|
||||
private final TenantAware.TenantResolver tenantResolver;
|
||||
|
||||
TenantIdentifier(final TenantAware.TenantResolver tenantResolver) {
|
||||
this.tenantResolver = tenantResolver;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String resolveCurrentTenantIdentifier() {
|
||||
// on bootstrapping hibernate requests tenant and want to be non-null
|
||||
return Optional.ofNullable(tenantResolver.resolveTenant()).map(String::toUpperCase).orElse("");
|
||||
return Optional.ofNullable(AccessContext.tenant()).map(String::toUpperCase).orElse("");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,8 +22,8 @@ import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantNotExistException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.hibernate.annotations.TenantId;
|
||||
|
||||
@@ -46,7 +46,7 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
private String tenant;
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* AccessContext aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* multi-schema based data separation setup to have the same primary key for different entities of different tenants.
|
||||
*/
|
||||
@Override
|
||||
@@ -55,7 +55,7 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
}
|
||||
|
||||
/**
|
||||
* Tenant aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* AccessContext aware entities extend the equals/hashcode strategy with the tenant name. That would allow for instance in a
|
||||
* multi-schema based data separation setup to have the same primary key for different entities of
|
||||
* different tenants.
|
||||
*/
|
||||
@@ -82,13 +82,13 @@ public abstract class AbstractJpaTenantAwareBaseEntity extends AbstractJpaBaseEn
|
||||
*/
|
||||
@PrePersist
|
||||
void prePersist() {
|
||||
// before persisting the entity check the current ID of the tenant by using the TenantAware service
|
||||
final String currentTenant = TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant();
|
||||
// before persisting the entity check the current ID of the tenant by using the AccessContext
|
||||
final String currentTenant = AccessContext.tenant();
|
||||
if (currentTenant == null) {
|
||||
throw new TenantNotExistException(
|
||||
String.format(
|
||||
"Tenant %s does not exists, cannot create entity %s with id %d",
|
||||
TenantAwareHolder.getInstance().getTenantAware().getCurrentTenant(), getClass(), getId()));
|
||||
"AccessContext %s does not exists, cannot create entity %s with id %d",
|
||||
AccessContext.tenant(), getClass(), getId()));
|
||||
}
|
||||
setTenant(currentTenant.toUpperCase());
|
||||
}
|
||||
|
||||
@@ -11,11 +11,12 @@ package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.RolloutApprovalStrategy;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
@@ -26,34 +27,19 @@ import org.springframework.security.core.context.SecurityContextHolder;
|
||||
* as the roles of the user who created the Rollout. Provides a no-operation implementation of
|
||||
* {@link RolloutApprovalStrategy#onApprovalRequired(Rollout)}.
|
||||
*/
|
||||
@NoArgsConstructor(access = AccessLevel.PACKAGE)
|
||||
public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
||||
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
DefaultRolloutApprovalStrategy(
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final SystemSecurityContext systemSecurityContext) {
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true, if rollout approval is enabled and rollout creator doesn't have approval role. It have to be called in the user context
|
||||
* Returns true, if rollout approval is enabled and rollout creator doesn't have approval role. It has to be called in the user context
|
||||
*/
|
||||
@Override
|
||||
public boolean isApprovalNeeded(final Rollout rollout) {
|
||||
return isApprovalEnabled() && hasNoApproveRolloutPermission(
|
||||
getCurrentAuthentication().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList());
|
||||
return TenantConfigHelper.getAsSystem(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class) &&
|
||||
hasNoApproveRolloutPermission(
|
||||
getCurrentAuthentication().getAuthorities().stream().map(GrantedAuthority::getAuthority).toList());
|
||||
}
|
||||
|
||||
/***
|
||||
* Per default do nothing.
|
||||
*
|
||||
* @param rollout
|
||||
* rollout to create approval task for.
|
||||
*/
|
||||
@Override
|
||||
public void onApprovalRequired(final Rollout rollout) {
|
||||
// do nothing per default, can be extended by further implementations.
|
||||
@@ -71,9 +57,4 @@ public class DefaultRolloutApprovalStrategy implements RolloutApprovalStrategy {
|
||||
private static boolean hasNoApproveRolloutPermission(final Collection<String> authorities) {
|
||||
return authorities.stream().noneMatch(SpPermission.APPROVE_ROLLOUT::equals);
|
||||
}
|
||||
|
||||
private boolean isApprovalEnabled() {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement
|
||||
.getConfigurationValue(TenantConfigurationKey.ROLLOUT_APPROVAL_ENABLED, Boolean.class).getValue());
|
||||
}
|
||||
}
|
||||
@@ -39,15 +39,12 @@ import org.eclipse.hawkbit.repository.RolloutExecutor;
|
||||
import org.eclipse.hawkbit.repository.RolloutHandler;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
||||
import org.eclipse.hawkbit.repository.SecurityTokenGeneratorHolder;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.event.ApplicationEventFilter;
|
||||
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
|
||||
import org.eclipse.hawkbit.repository.event.remote.EventEntityManagerHolder;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetPollEvent;
|
||||
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.aspects.ExceptionMappingAspectHandler;
|
||||
import org.eclipse.hawkbit.repository.jpa.autocleanup.AutoActionCleanup;
|
||||
@@ -64,7 +61,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.EntityInterceptorHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.helper.TenantAwareHolder;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ArtifactRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetRepository;
|
||||
@@ -82,7 +78,6 @@ import org.eclipse.hawkbit.repository.jpa.rollout.condition.StartNextGroupRollou
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupErrorCondition;
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.condition.ThresholdRolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.jpa.scheduler.AutoAssignScheduler;
|
||||
import org.eclipse.hawkbit.repository.jpa.scheduler.JpaAutoAssignExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.scheduler.JpaRolloutHandler;
|
||||
import org.eclipse.hawkbit.repository.jpa.scheduler.RolloutScheduler;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.ExceptionMapper;
|
||||
@@ -90,9 +85,6 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
|
||||
import org.eclipse.hawkbit.security.HawkbitSecurityProperties;
|
||||
import org.eclipse.hawkbit.security.SecurityTokenGenerator;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
@@ -222,17 +214,15 @@ public class JpaRepositoryConfiguration {
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
PauseRolloutGroupAction pauseRolloutGroupAction(
|
||||
final RolloutManagement rolloutManagement, final RolloutGroupRepository rolloutGroupRepository,
|
||||
final SystemSecurityContext systemSecurityContext) {
|
||||
return new PauseRolloutGroupAction(rolloutManagement, rolloutGroupRepository, systemSecurityContext);
|
||||
final RolloutManagement rolloutManagement, final RolloutGroupRepository rolloutGroupRepository) {
|
||||
return new PauseRolloutGroupAction(rolloutManagement, rolloutGroupRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
StartNextGroupRolloutGroupSuccessAction startNextRolloutGroupAction(
|
||||
final RolloutGroupRepository rolloutGroupRepository, final DeploymentManagement deploymentManagement,
|
||||
final SystemSecurityContext systemSecurityContext) {
|
||||
return new StartNextGroupRolloutGroupSuccessAction(rolloutGroupRepository, deploymentManagement, systemSecurityContext);
|
||||
final RolloutGroupRepository rolloutGroupRepository, final DeploymentManagement deploymentManagement) {
|
||||
return new StartNextGroupRolloutGroupSuccessAction(rolloutGroupRepository, deploymentManagement);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -260,8 +250,8 @@ public class JpaRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
SystemManagementCacheKeyGenerator systemManagementCacheKeyGenerator(final TenantAware tenantAware) {
|
||||
return new SystemManagementCacheKeyGenerator(tenantAware);
|
||||
SystemManagementCacheKeyGenerator systemManagementCacheKeyGenerator() {
|
||||
return new SystemManagementCacheKeyGenerator();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -270,10 +260,10 @@ public class JpaRepositoryConfiguration {
|
||||
return new PropertiesQuotaManagement(securityProperties);
|
||||
}
|
||||
|
||||
// register as bean in order to be registered event listeners
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RolloutStatusCache rolloutStatusCache(final TenantAware tenantAware) {
|
||||
return new RolloutStatusCache(tenantAware);
|
||||
RolloutStatusCache rolloutStatusCache() {
|
||||
return new RolloutStatusCache();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -282,46 +272,6 @@ public class JpaRepositoryConfiguration {
|
||||
return e -> e instanceof TargetPollEvent && !repositoryProperties.isPublishTargetPollEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SystemSecurityContext} singleton bean which make it
|
||||
* accessible in beans which cannot access the service directly, e.g.
|
||||
* JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||
return SystemSecurityContextHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantConfigurationManagement} singleton bean which make
|
||||
* it accessible in beans which cannot access the service directly, e.g.
|
||||
* JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||
return TenantConfigurationManagementHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link TenantAwareHolder} singleton bean which holds the current
|
||||
* {@link TenantAware} service and make it accessible in beans which
|
||||
* cannot access the service directly, e.g. JPA entities.
|
||||
*/
|
||||
@Bean
|
||||
TenantAwareHolder tenantAwareHolder() {
|
||||
return TenantAwareHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the {@link SecurityTokenGeneratorHolder} singleton bean which holds
|
||||
* the current {@link SecurityTokenGenerator} service and make it
|
||||
* accessible in beans which cannot access the service via injection
|
||||
*/
|
||||
@Bean
|
||||
SecurityTokenGeneratorHolder securityTokenGeneratorHolder() {
|
||||
return SecurityTokenGeneratorHolder.getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the singleton instance of the {@link EntityInterceptorHolder}
|
||||
*/
|
||||
@@ -340,10 +290,10 @@ public class JpaRepositoryConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RolloutHandler rolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
|
||||
RolloutHandler rolloutHandler(final RolloutManagement rolloutManagement,
|
||||
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
|
||||
final PlatformTransactionManager txManager, final Optional<MeterRegistry> meterRegistry) {
|
||||
return new JpaRolloutHandler(tenantAware, rolloutManagement, rolloutExecutor, lockRegistry, txManager, meterRegistry);
|
||||
return new JpaRolloutHandler(rolloutManagement, rolloutExecutor, lockRegistry, txManager, meterRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -353,9 +303,8 @@ public class JpaRepositoryConfiguration {
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
RolloutApprovalStrategy rolloutApprovalStrategy(
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final SystemSecurityContext systemSecurityContext) {
|
||||
return new DefaultRolloutApprovalStrategy(tenantConfigurationManagement, systemSecurityContext);
|
||||
RolloutApprovalStrategy rolloutApprovalStrategy() {
|
||||
return new DefaultRolloutApprovalStrategy();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -372,44 +321,36 @@ public class JpaRepositoryConfiguration {
|
||||
/**
|
||||
* {@link EventEntityManager} bean.
|
||||
*
|
||||
* @param aware the tenant aware
|
||||
* @param entityManager the entity manager
|
||||
* @return a new {@link EventEntityManager}
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
EventEntityManager eventEntityManager(final TenantAware aware, final EntityManager entityManager) {
|
||||
return new JpaEventEntityManager(aware, entityManager);
|
||||
EventEntityManager eventEntityManager(final EntityManager entityManager) {
|
||||
return new JpaEventEntityManager(entityManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AutoAssignScheduler} bean.
|
||||
* <p/>
|
||||
* Note: does not activate in test profile, otherwise it is hard to test the
|
||||
* auto assign functionality.
|
||||
*
|
||||
* @param systemManagement to find all tenants
|
||||
* @param systemSecurityContext to run as system
|
||||
* @param autoAssignExecutor to run a check as tenant
|
||||
* @param lockRegistry to lock the tenant for auto assignment
|
||||
* @return a new {@link JpaAutoAssignExecutor}
|
||||
* Note: does not activate in test profile, otherwise it is hard to test the auto assign functionality.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
// don't active the auto assign scheduler in test, otherwise it is hard to test
|
||||
@Profile("!test")
|
||||
@ConditionalOnProperty(prefix = "hawkbit.autoassign.scheduler", name = "enabled", matchIfMissing = true)
|
||||
AutoAssignScheduler autoAssignScheduler(final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor,
|
||||
AutoAssignScheduler autoAssignScheduler(
|
||||
final SystemManagement systemManagement, final AutoAssignExecutor autoAssignExecutor,
|
||||
final LockRegistry lockRegistry, final Optional<MeterRegistry> meterRegistry) {
|
||||
return new AutoAssignScheduler(systemManagement, systemSecurityContext, autoAssignExecutor, lockRegistry, meterRegistry);
|
||||
return new AutoAssignScheduler(systemManagement, autoAssignExecutor, lockRegistry, meterRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AutoActionCleanup} bean.
|
||||
*
|
||||
* @param deploymentManagement Deployment management service
|
||||
* @param configManagement Tenant configuration service
|
||||
* @param configManagement AccessContext configuration service
|
||||
* @return a new {@link AutoActionCleanup} bean
|
||||
*/
|
||||
@Bean
|
||||
@@ -418,44 +359,29 @@ public class JpaRepositoryConfiguration {
|
||||
return new AutoActionCleanup(deploymentManagement, configManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AutoCleanupScheduler} bean.
|
||||
*
|
||||
* @param systemManagement to find all tenants
|
||||
* @param systemSecurityContext to run as system
|
||||
* @param lockRegistry to lock the tenant for auto assignment
|
||||
* @param cleanupTasks a list of cleanup tasks
|
||||
* @return a new {@link AutoCleanupScheduler} bean
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Profile("!test")
|
||||
@ConditionalOnProperty(prefix = "hawkbit.autocleanup.scheduler", name = "enabled", matchIfMissing = true)
|
||||
AutoCleanupScheduler autoCleanupScheduler(
|
||||
final List<AutoCleanupScheduler.CleanupTask> cleanupTasks,
|
||||
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, final LockRegistry lockRegistry) {
|
||||
return new AutoCleanupScheduler(cleanupTasks, systemManagement, systemSecurityContext, lockRegistry);
|
||||
final SystemManagement systemManagement, final LockRegistry lockRegistry) {
|
||||
return new AutoCleanupScheduler(cleanupTasks, systemManagement, lockRegistry);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link RolloutScheduler} bean.
|
||||
* <p/>
|
||||
* Note: does not activate in test profile, otherwise it is hard to test the
|
||||
* rollout handling functionality.
|
||||
*
|
||||
* @param systemManagement to find all tenants
|
||||
* @param rolloutHandler to run the rollout handler
|
||||
* @param systemSecurityContext to run as system
|
||||
* @return a new {@link RolloutScheduler} bean.
|
||||
* Note: does not activate in test profile, otherwise it is hard to test the rollout handling functionality.
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
@Profile("!test")
|
||||
@ConditionalOnProperty(prefix = "hawkbit.rollout.scheduler", name = "enabled", matchIfMissing = true)
|
||||
RolloutScheduler rolloutScheduler(
|
||||
final SystemManagement systemManagement, final RolloutHandler rolloutHandler, final SystemSecurityContext systemSecurityContext,
|
||||
final SystemManagement systemManagement, final RolloutHandler rolloutHandler,
|
||||
@Value("${hawkbit.rollout.executor.thread-pool.size:1}") final int threadPoolSize, final Optional<MeterRegistry> meterRegistry) {
|
||||
return new RolloutScheduler(rolloutHandler, systemManagement, systemSecurityContext, threadPoolSize, meterRegistry);
|
||||
return new RolloutScheduler(rolloutHandler, systemManagement, threadPoolSize, meterRegistry);
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -15,7 +15,8 @@ import java.util.Optional;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import lombok.NonNull;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.cache.interceptor.SimpleKeyGenerator;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -26,11 +27,6 @@ import org.springframework.context.annotation.Bean;
|
||||
public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyGenerator {
|
||||
|
||||
private final ThreadLocal<String> createInitialTenant = new ThreadLocal<>();
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
public SystemManagementCacheKeyGenerator(final TenantAware tenantAware) {
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Bean
|
||||
@@ -70,16 +66,19 @@ public class SystemManagementCacheKeyGenerator implements CurrentTenantCacheKeyG
|
||||
/**
|
||||
* An implementation of the {@link KeyGenerator} to generate a key based on
|
||||
* either the {@code createInitialTenant} thread local and the
|
||||
* {@link TenantAware}, but in case we are in a tenant creation with its default
|
||||
* {@link AccessContext}, but in case we are in a tenant creation with its default
|
||||
* types we need to use as the tenant the current tenant which is currently
|
||||
* created and not the one currently in the {@link TenantAware}.
|
||||
* created and not the one currently in the {@link AccessContext}.
|
||||
*/
|
||||
public class CurrentTenantKeyGenerator implements KeyGenerator {
|
||||
|
||||
@Override
|
||||
public Object generate(final Object target, final Method method, final Object... params) {
|
||||
String tenant = getTenantInCreation().orElseGet(tenantAware::getCurrentTenant).toUpperCase();
|
||||
public Object generate(@NonNull final Object target, @NonNull final Method method, @NonNull final Object... params) {
|
||||
final String tenant = Objects.requireNonNull(
|
||||
getTenantInCreation().orElseGet(AccessContext::tenant),
|
||||
"CurrentTenantKeyGenerator.generate called not in tenant context")
|
||||
.toUpperCase();
|
||||
return SimpleKeyGenerator.generateKey(tenant, tenant);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,9 @@
|
||||
package org.eclipse.hawkbit.repository.jpa;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.springframework.cache.interceptor.KeyGenerator;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@@ -21,14 +22,11 @@ import org.springframework.stereotype.Service;
|
||||
@Service
|
||||
public class TenantKeyGenerator implements KeyGenerator {
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
public TenantKeyGenerator(final TenantAware tenantAware) {
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object generate(final Object target, final Method method, final Object... params) {
|
||||
return tenantAware.getCurrentTenant().toUpperCase();
|
||||
return Objects.requireNonNull(
|
||||
AccessContext.tenant(),
|
||||
"TenantKeyGenerator.generate called not in tenant context")
|
||||
.toUpperCase();
|
||||
}
|
||||
}
|
||||
@@ -21,13 +21,7 @@ import jakarta.persistence.criteria.Root;
|
||||
import jakarta.persistence.metamodel.EntityType;
|
||||
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetTypeFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction_;
|
||||
@@ -37,8 +31,12 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetType;
|
||||
import org.eclipse.hawkbit.security.SecurityContextSerializer;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetTypeFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
@@ -60,12 +58,6 @@ import org.springframework.util.function.SingletonSupplier;
|
||||
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.enabled", havingValue = "true")
|
||||
public class AccessControllerConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
SecurityContextSerializer securityContextSerializer() {
|
||||
return SecurityContextSerializer.JSON_SERIALIZATION;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnProperty(name = "hawkbit.acm.access-controller.target.enabled", havingValue = "true", matchIfMissing = true)
|
||||
AccessController<JpaTarget> targetAccessController() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import java.util.Set;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModule;
|
||||
|
||||
@@ -18,10 +18,10 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ql.QueryField;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.security.core.GrantedAuthority;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
@@ -48,7 +48,7 @@ public class DefaultAccessController<A extends Enum<A> & QueryField, T> implemen
|
||||
|
||||
@Override
|
||||
public Optional<Specification<T>> getAccessRules(final Operation operation) {
|
||||
if (SystemSecurityContext.isCurrentThreadSystemCode()) {
|
||||
if (AccessContext.isCurrentThreadSystemCode()) {
|
||||
// system code - no restrictions. this runs with SYSTEM_ROLE, so no restrictions apply anyway - not scopes, but this way should be faster
|
||||
return Optional.empty();
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public class DefaultAccessController<A extends Enum<A> & QueryField, T> implemen
|
||||
|
||||
@Override
|
||||
public void assertOperationAllowed(final Operation operation, final T entity) throws InsufficientPermissionException {
|
||||
if (SystemSecurityContext.isCurrentThreadSystemCode()) {
|
||||
if (AccessContext.isCurrentThreadSystemCode()) {
|
||||
// system code - no restrictions. this runs with SYSTEM_ROLE, so no restrictions apply anyway - not scopes, but this way should be faster
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,13 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.autocleanup;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
|
||||
@@ -29,7 +30,6 @@ public class AutoCleanupScheduler {
|
||||
private static final String PROP_AUTO_CLEANUP_INTERVAL = "${hawkbit.autocleanup.scheduler.fixedDelay:86400000}";
|
||||
|
||||
private final SystemManagement systemManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final LockRegistry lockRegistry;
|
||||
private final List<CleanupTask> cleanupTasks;
|
||||
|
||||
@@ -38,14 +38,12 @@ public class AutoCleanupScheduler {
|
||||
*
|
||||
* @param cleanupTasks A list of cleanup tasks.
|
||||
* @param systemManagement Management APIs to invoke actions in a certain tenant context.
|
||||
* @param systemSecurityContext The system security context.
|
||||
* @param lockRegistry A registry for shared locks.
|
||||
*/
|
||||
public AutoCleanupScheduler(
|
||||
final List<CleanupTask> cleanupTasks,
|
||||
final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext, final LockRegistry lockRegistry) {
|
||||
final SystemManagement systemManagement, final LockRegistry lockRegistry) {
|
||||
this.systemManagement = systemManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.lockRegistry = lockRegistry;
|
||||
this.cleanupTasks = cleanupTasks;
|
||||
}
|
||||
@@ -58,7 +56,7 @@ public class AutoCleanupScheduler {
|
||||
log.debug("Auto cleanup scheduler has been triggered.");
|
||||
// run this code in system code privileged to have the necessary permission to query and create entities
|
||||
if (!cleanupTasks.isEmpty()) {
|
||||
systemSecurityContext.runAsSystem(this::executeAutoCleanup);
|
||||
asSystem(this::executeAutoCleanup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,11 +9,12 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.event;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.event.remote.EventEntityManager;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
@@ -22,22 +23,19 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
@Transactional(readOnly = true)
|
||||
public class JpaEventEntityManager implements EventEntityManager {
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
private final EntityManager entityManager;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param tenantAware the tenant aware
|
||||
* @param entityManager the entity manager
|
||||
*/
|
||||
public JpaEventEntityManager(final TenantAware tenantAware, final EntityManager entityManager) {
|
||||
this.tenantAware = tenantAware;
|
||||
public JpaEventEntityManager(final EntityManager entityManager) {
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <E extends TenantAwareBaseEntity> E findEntity(final String tenant, final Long id, final Class<E> entityType) {
|
||||
return tenantAware.runAsTenant(tenant, () -> entityManager.find(entityType, id));
|
||||
return asSystemAsTenant(tenant, () -> entityManager.find(entityType, id));
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import java.util.function.Consumer;
|
||||
import jakarta.persistence.criteria.JoinType;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
@@ -87,7 +88,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
public JpaAction createTargetAction(
|
||||
final String initiatedBy, final TargetWithActionType targetWithActionType,
|
||||
final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
final Optional<JpaTarget> optTarget = targets.stream()
|
||||
.filter(t -> t.getControllerId().equals(targetWithActionType.getControllerId())).findFirst();
|
||||
@@ -108,7 +109,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
actionForTarget.setMaintenanceWindowSchedule(targetWithActionType.getMaintenanceSchedule());
|
||||
actionForTarget.setMaintenanceWindowDuration(targetWithActionType.getMaintenanceWindowDuration());
|
||||
actionForTarget.setMaintenanceWindowTimeZone(targetWithActionType.getMaintenanceWindowTimeZone());
|
||||
actionForTarget.setInitiatedBy(initiatedBy);
|
||||
actionForTarget.setInitiatedBy(AccessContext.actor());
|
||||
return actionForTarget;
|
||||
}).orElseGet(() -> {
|
||||
log.warn("Cannot find target for targetWithActionType '{}'.", targetWithActionType.getControllerId());
|
||||
@@ -234,10 +235,8 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
*
|
||||
* @param distributionSet to set
|
||||
* @param targetIds to change
|
||||
* @param currentUser for auditing
|
||||
*/
|
||||
abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet distributionSet,
|
||||
final List<List<Long>> targetIds, final String currentUser);
|
||||
abstract void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet distributionSet, final List<List<Long>> targetIds);
|
||||
|
||||
/**
|
||||
* Cancels actions that can be canceled (i.e.
|
||||
@@ -265,8 +264,7 @@ public abstract class AbstractDsAssignmentStrategy {
|
||||
final RolloutGroup rolloutGroup = action.getRolloutGroup();
|
||||
if (rolloutGroup != null) {
|
||||
final Rollout rollout = rolloutGroup.getRollout();
|
||||
return String.format("Initiated by Rollout Group '%s' [Rollout %s:%s]", rolloutGroup.getName(),
|
||||
rollout.getName(), rollout.getId());
|
||||
return String.format("Initiated by Rollout Group '%s' [Rollout %s:%s]", rolloutGroup.getName(), rollout.getName(), rollout.getId());
|
||||
}
|
||||
return String.format("Assignment initiated by user '%s'", action.getInitiatedBy());
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.eclipse.hawkbit.artifact.exception.HashNotMatchException;
|
||||
import org.eclipse.hawkbit.artifact.model.ArtifactHashes;
|
||||
import org.eclipse.hawkbit.artifact.model.ArtifactStream;
|
||||
import org.eclipse.hawkbit.artifact.model.StoredArtifactInfo;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
@@ -49,7 +50,6 @@ import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.ArtifactUpload;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
@@ -74,7 +74,6 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
private final SoftwareModuleRepository softwareModuleRepository;
|
||||
private final EntityManager entityManager;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final TenantAware tenantAware;
|
||||
private final QuotaManagement quotaManagement;
|
||||
|
||||
protected JpaArtifactManagement(
|
||||
@@ -83,15 +82,13 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
final SoftwareModuleRepository softwareModuleRepository,
|
||||
final EntityManager entityManager,
|
||||
final PlatformTransactionManager txManager,
|
||||
final QuotaManagement quotaManagement,
|
||||
final TenantAware tenantAware) {
|
||||
final QuotaManagement quotaManagement) {
|
||||
this.artifactRepository = artifactRepository;
|
||||
this.artifactStorage = artifactStorage.orElse(null);
|
||||
this.softwareModuleRepository = softwareModuleRepository;
|
||||
this.entityManager = entityManager;
|
||||
this.txManager = txManager;
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -135,7 +132,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
try {
|
||||
return storeArtifactMetadata(softwareModule, filename, artifact.getHashes(), artifact.getSize(), existing);
|
||||
} catch (final Exception e) {
|
||||
artifactStorage.deleteBySha1(tenantAware.getCurrentTenant(), artifact.getHashes().sha1());
|
||||
artifactStorage.deleteBySha1(AccessContext.tenant(), artifact.getHashes().sha1());
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -147,7 +144,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
final String tenant = AccessContext.tenant();
|
||||
// check access to the software module and if artifact belongs to it
|
||||
for (final Artifact artifact : softwareModuleRepository.getById(softwareModuleId).getArtifacts()) {
|
||||
if (artifact.getSha1Hash().equals(sha1Hash)) {
|
||||
@@ -203,13 +200,13 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
void clearArtifactBinary(final String sha1Hash) {
|
||||
DeploymentHelper.runInNewTransaction(txManager, "clearArtifactBinary", status -> {
|
||||
// countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse will skip ACM checks and will return total count as it should be
|
||||
if (artifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash, tenantAware.getCurrentTenant()) <= 0) {
|
||||
if (artifactRepository.countBySha1HashAndTenantAndSoftwareModuleDeletedIsFalse(sha1Hash, AccessContext.tenant()) <= 0) {
|
||||
// removes the real artifact ONLY AFTER the delete of artifact or software module
|
||||
// in local history has passed successfully (caller has permission and no errors)
|
||||
afterCommit(() -> {
|
||||
try {
|
||||
log.debug("deleting artifact from repository {}", sha1Hash);
|
||||
artifactStorage.deleteBySha1(tenantAware.getCurrentTenant(), sha1Hash);
|
||||
artifactStorage.deleteBySha1(AccessContext.tenant(), sha1Hash);
|
||||
} catch (final ArtifactStoreException e) {
|
||||
throw new ArtifactDeleteFailedException(e);
|
||||
}
|
||||
@@ -224,7 +221,7 @@ public class JpaArtifactManagement implements ArtifactManagement {
|
||||
try (final InputStream wrappedStream = wrapInQuotaStream(
|
||||
isSmEncrypted ? ArtifactEncryptionService.getInstance().encryptArtifact(artifactUpload.moduleId(), stream) : stream)) {
|
||||
return artifactStorage.store(
|
||||
tenantAware.getCurrentTenant(),
|
||||
AccessContext.tenant(),
|
||||
wrappedStream, artifactUpload.filename(),
|
||||
artifactUpload.contentType(), artifactUpload.hash());
|
||||
} catch (final ArtifactStoreException | IOException e) {
|
||||
|
||||
@@ -22,7 +22,6 @@ import jakarta.persistence.EntityManager;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.ConfirmationManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.exception.AutoConfirmationAlreadyActiveException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidConfirmationFeedbackException;
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asActor;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.DOWNLOADED;
|
||||
import static org.eclipse.hawkbit.repository.model.Action.Status.FINISHED;
|
||||
@@ -46,6 +49,7 @@ import jakarta.validation.constraints.NotEmpty;
|
||||
import lombok.Data;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.ConfirmationManagement;
|
||||
import org.eclipse.hawkbit.repository.ControllerManagement;
|
||||
@@ -55,9 +59,8 @@ import org.eclipse.hawkbit.repository.MaintenanceScheduleHelper;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.SecurityTokenGeneratorHolder;
|
||||
import org.eclipse.hawkbit.repository.SecurityTokenGenerator;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.UpdateMode;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.event.remote.CancelTargetAssignmentEvent;
|
||||
@@ -68,6 +71,7 @@ import org.eclipse.hawkbit.repository.exception.EntityAlreadyExistsException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidTargetAttributeException;
|
||||
import org.eclipse.hawkbit.repository.exception.SoftwareModuleNotAssignedToTargetException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.Jpa;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
@@ -101,13 +105,11 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.ControllerPollProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.eclipse.hawkbit.util.IpUtil;
|
||||
import org.eclipse.hawkbit.utils.IpUtil;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -137,8 +139,6 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
private static final Pattern PATTERN = Pattern.compile("[a-zA-Z0-9_\\-!@#$%^&*()+=\\[\\]{}|;:'\",.<>/\\\\?\\s]*");
|
||||
|
||||
private final BlockingDeque<TargetPoll> queue;
|
||||
|
||||
// TODO - make it final
|
||||
private TargetRepository targetRepository;
|
||||
private final TargetTypeRepository targetTypeRepository;
|
||||
@@ -147,14 +147,13 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
private final SoftwareModuleRepository softwareModuleRepository;
|
||||
private final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement;
|
||||
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final ControllerPollProperties controllerPollProperties;
|
||||
private final Duration minPollingTime;
|
||||
private final Duration maxPollingTime;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final EntityManager entityManager;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
private final Duration minPollingTime;
|
||||
private final Duration maxPollingTime;
|
||||
private final BlockingDeque<TargetPoll> queue;
|
||||
|
||||
@SuppressWarnings("squid:S00107")
|
||||
protected JpaControllerManagement(
|
||||
@@ -165,9 +164,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
final SoftwareModuleRepository softwareModuleRepository,
|
||||
final SoftwareModuleManagement<? extends SoftwareModule> softwareModuleManagement,
|
||||
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final ControllerPollProperties controllerPollProperties,
|
||||
final ControllerPollProperties controllerPollProperties,
|
||||
final PlatformTransactionManager txManager, final EntityManager entityManager,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware,
|
||||
final ScheduledExecutorService executorService) {
|
||||
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
|
||||
|
||||
@@ -178,19 +176,16 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
this.softwareModuleRepository = softwareModuleRepository;
|
||||
this.softwareModuleManagement = softwareModuleManagement;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.controllerPollProperties = controllerPollProperties;
|
||||
this.txManager = txManager;
|
||||
this.entityManager = entityManager;
|
||||
|
||||
minPollingTime = controllerPollProperties.getMinPollingTime() == null
|
||||
? Duration.of(0, ChronoUnit.SECONDS)
|
||||
: DurationHelper.fromString(controllerPollProperties.getMinPollingTime());
|
||||
maxPollingTime = controllerPollProperties.getMaxPollingTime() == null
|
||||
? Duration.of(100, ChronoUnit.YEARS)
|
||||
: DurationHelper.fromString(controllerPollProperties.getMaxPollingTime());
|
||||
this.txManager = txManager;
|
||||
this.entityManager = entityManager;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantAware = tenantAware;
|
||||
|
||||
if (!repositoryProperties.isEagerPollPersistence()) {
|
||||
executorService.scheduleWithFixedDelay(this::flushUpdateQueue,
|
||||
repositoryProperties.getPollPersistenceFlushTime(),
|
||||
@@ -277,7 +272,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
public Map<Long, Map<String, String>> findTargetVisibleMetaDataBySoftwareModuleId(final Collection<Long> moduleId) {
|
||||
return systemSecurityContext.runAsSystem(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(moduleId));
|
||||
return asSystem(() -> softwareModuleManagement.findMetaDataBySoftwareModuleIdsAndTargetVisible(moduleId));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -383,9 +378,11 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
|
||||
@Override
|
||||
public String getPollingTime(final Target target) {
|
||||
return systemSecurityContext.runAsSystem(() -> {
|
||||
// as system so to be able to read tenant configuration (READ_TENANT_CONFIGURATION)
|
||||
return asSystem(() -> {
|
||||
final PollingTime pollingTime = new PollingTime(
|
||||
tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class).getValue());
|
||||
TenantConfigHelper.getTenantConfigurationManagement()
|
||||
.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class).getValue());
|
||||
if (!ObjectUtils.isEmpty(pollingTime.getOverrides()) && target instanceof JpaTarget jpaTarget) {
|
||||
for (final PollingTime.Override override : pollingTime.getOverrides()) {
|
||||
try {
|
||||
@@ -409,9 +406,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
return pollingTime;
|
||||
} else {
|
||||
// the count to be used for reducing polling interval -> the configured value of {@link TenantConfigurationKey#MAINTENANCE_WINDOW_POLL_COUNT}
|
||||
final int maintenanceWindowPollCount = systemSecurityContext.runAsSystem(
|
||||
() -> tenantConfigurationManagement.getConfigurationValue(
|
||||
TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class).getValue());
|
||||
final int maintenanceWindowPollCount = TenantConfigHelper.getAsSystem(
|
||||
TenantConfigurationKey.MAINTENANCE_WINDOW_POLL_COUNT, Integer.class);
|
||||
return new EventTimer(pollingTime, controllerPollProperties.getMinPollingTime(), ChronoUnit.SECONDS)
|
||||
.timeToNextEvent(maintenanceWindowPollCount, action.getMaintenanceWindowStartTime().orElse(null));
|
||||
}
|
||||
@@ -524,7 +520,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
jpaAction.setStatus(Status.CANCELING);
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(
|
||||
new JpaActionStatus(jpaAction, Status.CANCELING, System.currentTimeMillis(), "manual cancelation requested"));
|
||||
new JpaActionStatus(jpaAction, Status.CANCELING, java.lang.System.currentTimeMillis(), "manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(jpaAction);
|
||||
|
||||
cancelAssignDistributionSetEvent(jpaAction);
|
||||
@@ -572,9 +568,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
@Override
|
||||
public boolean updateOfflineAssignedVersion(@NotEmpty final String controllerId, final String distributionName, final String version) {
|
||||
List<DistributionSetAssignmentResult> distributionSetAssignmentResults =
|
||||
systemSecurityContext.runAsSystem(() -> deploymentManagement.offlineAssignedDistributionSets(
|
||||
controllerId,
|
||||
List.of(Map.entry(controllerId, distributionSetManagement.findByNameAndVersion(distributionName, version).getId()))));
|
||||
asSystem(() -> asActor(controllerId, () -> deploymentManagement.offlineAssignedDistributionSets(
|
||||
List.of(Map.entry(controllerId, distributionSetManagement.findByNameAndVersion(distributionName, version).getId())))));
|
||||
|
||||
return distributionSetAssignmentResults.stream()
|
||||
.findFirst()
|
||||
@@ -656,9 +651,9 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
jpaTarget.setControllerId(controllerId);
|
||||
jpaTarget.setDescription("Plug and Play target: " + controllerId);
|
||||
jpaTarget.setName((StringUtils.hasText(name) ? name : controllerId));
|
||||
jpaTarget.setSecurityToken(SecurityTokenGeneratorHolder.getInstance().generateToken());
|
||||
jpaTarget.setSecurityToken(SecurityTokenGenerator.generateToken());
|
||||
jpaTarget.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
||||
jpaTarget.setLastTargetQuery(System.currentTimeMillis());
|
||||
jpaTarget.setLastTargetQuery(java.lang.System.currentTimeMillis());
|
||||
jpaTarget.setAddress(Optional.ofNullable(address).map(URI::toString).orElse(null));
|
||||
|
||||
if (StringUtils.hasText(type)) {
|
||||
@@ -701,8 +696,8 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
try {
|
||||
events.stream().collect(Collectors.groupingBy(TargetPoll::getTenant)).forEach((tenant, polls) -> {
|
||||
final TransactionCallback<Void> createTransaction = status -> updateLastTargetQueries(tenant, polls);
|
||||
tenantAware.runAsTenant(tenant,
|
||||
() -> DeploymentHelper.runInNewTransaction(txManager, "flushUpdateQueue", createTransaction));
|
||||
asSystemAsTenant(
|
||||
tenant, () -> DeploymentHelper.runInNewTransaction(txManager, "flushUpdateQueue", createTransaction));
|
||||
});
|
||||
} catch (final RuntimeException ex) {
|
||||
log.error("Failed to persist UpdateQueue content.", ex);
|
||||
@@ -720,7 +715,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
Constants.MAX_ENTRIES_IN_STATEMENT);
|
||||
|
||||
pollChunks.forEach(chunk -> {
|
||||
setLastTargetQuery(tenant, System.currentTimeMillis(), chunk);
|
||||
setLastTargetQuery(tenant, java.lang.System.currentTimeMillis(), chunk);
|
||||
chunk.forEach(controllerId -> afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new TargetPollEvent(controllerId, tenant))));
|
||||
});
|
||||
@@ -781,7 +776,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
if (isStatusUnknown(toUpdate.getUpdateStatus())) {
|
||||
toUpdate.setUpdateStatus(TargetUpdateStatus.REGISTERED);
|
||||
}
|
||||
toUpdate.setLastTargetQuery(System.currentTimeMillis());
|
||||
toUpdate.setLastTargetQuery(java.lang.System.currentTimeMillis());
|
||||
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new TargetPollEvent(toUpdate)));
|
||||
return targetRepository.save(toUpdate);
|
||||
}
|
||||
@@ -827,7 +822,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
target.setRequestControllerAttributes(true);
|
||||
|
||||
EventPublisherHolder.getInstance().getEventPublisher()
|
||||
.publishEvent(new TargetAttributesRequestedEvent(tenantAware.getCurrentTenant(), target.getId(),
|
||||
.publishEvent(new TargetAttributesRequestedEvent(AccessContext.tenant(), target.getId(),
|
||||
JpaTarget.class, target.getControllerId(), target.getAddress() != null ? target.getAddress() : null));
|
||||
}
|
||||
|
||||
@@ -908,7 +903,7 @@ public class JpaControllerManagement extends JpaActionManagement implements Cont
|
||||
// case controller retrieves a action multiple times.
|
||||
if (resultList.isEmpty() || (Status.RETRIEVED != resultList.get(0)[1])) {
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.RETRIEVED, System.currentTimeMillis(), message));
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.RETRIEVED, java.lang.System.currentTimeMillis(), message));
|
||||
|
||||
// don't change the action status itself in case the action is in
|
||||
// canceling state otherwise
|
||||
|
||||
@@ -9,9 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -38,12 +38,12 @@ import jakarta.persistence.criteria.Root;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetAssignDistributionSetEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.CancelActionNotAllowedException;
|
||||
@@ -53,6 +53,7 @@ import org.eclipse.hawkbit.repository.exception.IncompatibleTargetTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.exception.MultiAssignmentIsNotEnabledException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.Jpa;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
@@ -86,14 +87,10 @@ import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TargetWithActionType;
|
||||
import org.eclipse.hawkbit.repository.qfields.ActionFields;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
@@ -132,34 +129,30 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
"DELETE FROM sp_action " + "WHERE id IN (SELECT id FROM sp_action " + "WHERE tenant=" + Jpa.nativeQueryParamPrefix() + "tenant" + " AND status IN (%s)" + " AND last_modified_at<" + Jpa.nativeQueryParamPrefix() + "last_modified_at LIMIT " + ACTION_PAGE_LIMIT + ")");
|
||||
}
|
||||
|
||||
private final EntityManager entityManager;
|
||||
private final JpaDistributionSetManagement distributionSetManagement;
|
||||
private final TargetRepository targetRepository;
|
||||
private final AuditorAware<String> auditorProvider;
|
||||
private final EntityManager entityManager;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final Database database;
|
||||
|
||||
private final RetryTemplate retryTemplate;
|
||||
private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy;
|
||||
private final OfflineDsAssignmentStrategy offlineDsAssignmentStrategy;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final TenantAware tenantAware;
|
||||
private final AuditorAware<String> auditorAware;
|
||||
private final Database database;
|
||||
private final RetryTemplate retryTemplate;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
protected JpaDeploymentManagement(final EntityManager entityManager, final ActionRepository actionRepository,
|
||||
protected JpaDeploymentManagement(
|
||||
final ActionRepository actionRepository, final ActionStatusRepository actionStatusRepository,
|
||||
final QuotaManagement quotaManagement, final RepositoryProperties repositoryProperties,
|
||||
final JpaDistributionSetManagement distributionSetManagement, final TargetRepository targetRepository,
|
||||
final ActionStatusRepository actionStatusRepository, final AuditorAware<String> auditorProvider,
|
||||
final PlatformTransactionManager txManager,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement, final QuotaManagement quotaManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final AuditorAware<String> auditorAware,
|
||||
final JpaProperties jpaProperties, final RepositoryProperties repositoryProperties) {
|
||||
final EntityManager entityManager, final PlatformTransactionManager txManager, final JpaProperties jpaProperties) {
|
||||
super(actionRepository, actionStatusRepository, quotaManagement, repositoryProperties);
|
||||
this.entityManager = entityManager;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.targetRepository = targetRepository;
|
||||
this.auditorProvider = auditorProvider;
|
||||
this.entityManager = entityManager;
|
||||
this.txManager = txManager;
|
||||
this.database = jpaProperties.getDatabase();
|
||||
|
||||
retryTemplate = createRetryTemplate();
|
||||
final Consumer<MaxAssignmentsExceededInfo> maxAssignmentsExceededHandler = maxAssignmentsExceededInfo ->
|
||||
handleMaxAssignmentsExceeded(
|
||||
maxAssignmentsExceededInfo.targetId,
|
||||
@@ -171,51 +164,26 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
offlineDsAssignmentStrategy = new OfflineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
|
||||
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties,
|
||||
maxAssignmentsExceededHandler);
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantAware = tenantAware;
|
||||
this.auditorAware = auditorAware;
|
||||
this.database = jpaProperties.getDatabase();
|
||||
this.retryTemplate = createRetryTemplate();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(final List<DeploymentRequest> deploymentRequests) {
|
||||
return assignDistributionSets0(tenantAware.getCurrentUsername(), deploymentRequests, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final String initiatedBy, final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
return assignDistributionSets0(initiatedBy, deploymentRequests, actionMessage);
|
||||
}
|
||||
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets0(
|
||||
final String initiatedBy, final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(deploymentRequests);
|
||||
return assignDistributionSets(initiatedBy, deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage) {
|
||||
WeightValidationHelper.validate(deploymentRequests);
|
||||
return assignDistributionSets(deploymentRequests, actionMessage, onlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(
|
||||
final String initiatedBy, final Collection<Entry<String, Long>> assignments) {
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(final Collection<Entry<String, Long>> assignments) {
|
||||
final Collection<Entry<String, Long>> distinctAssignments = assignments.stream().distinct().toList();
|
||||
enforceMaxAssignmentsPerRequest(distinctAssignments.size());
|
||||
|
||||
final List<DeploymentRequest> deploymentRequests = distinctAssignments.stream()
|
||||
.map(entry -> DeploymentRequest.builder(entry.getKey(), entry.getValue()).build()).toList();
|
||||
|
||||
return assignDistributionSets(
|
||||
auditorAware.getCurrentAuditor().orElse(tenantAware.getCurrentUsername()), deploymentRequests, null,
|
||||
offlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(isolation = Isolation.READ_COMMITTED)
|
||||
public List<DistributionSetAssignmentResult> offlineAssignedDistributionSets(final Collection<Entry<String, Long>> assignments) {
|
||||
return offlineAssignedDistributionSets(tenantAware.getCurrentUsername(), assignments);
|
||||
return assignDistributionSets(deploymentRequests, null, offlineDsAssignmentStrategy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -242,7 +210,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
action.setStatus(Status.CANCELING);
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, System.currentTimeMillis(),
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELING, java.lang.System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "manual cancelation requested"));
|
||||
final Action saveAction = actionRepository.save(action);
|
||||
|
||||
@@ -384,7 +352,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
log.warn("action ({}) was still active and has been force quite.", action);
|
||||
|
||||
// document that the status has been retrieved
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, System.currentTimeMillis(),
|
||||
actionStatusRepository.save(new JpaActionStatus(action, Status.CANCELED, java.lang.System.currentTimeMillis(),
|
||||
RepositoryConstants.SERVER_MESSAGE_PREFIX + "A force quit has been performed."));
|
||||
|
||||
DeploymentHelper.successCancellation(action, actionRepository, targetRepository);
|
||||
@@ -533,7 +501,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
String.format(getQueryForDeleteActionsByStatusAndLastModifiedBeforeString(database),
|
||||
Jpa.formatNativeQueryInClause("status", statusList)));
|
||||
|
||||
deleteQuery.setParameter("tenant", tenantAware.getCurrentTenant().toUpperCase());
|
||||
deleteQuery.setParameter("tenant", AccessContext.tenant().toUpperCase());
|
||||
Jpa.setNativeQueryInParameter(deleteQuery, "status", statusList);
|
||||
deleteQuery.setParameter("last_modified_at", lastModified);
|
||||
|
||||
@@ -603,8 +571,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
}
|
||||
|
||||
;
|
||||
|
||||
/**
|
||||
* Deletes the first n target actions of a target
|
||||
*
|
||||
@@ -628,11 +594,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private int getActionsPurgePercentage() {
|
||||
return getConfigValue(TenantConfigurationKey.ACTION_CLEANUP_ON_QUOTA_HIT_PERCENTAGE, Integer.class);
|
||||
return TenantConfigHelper.getAsSystem(TenantConfigurationKey.ACTION_CLEANUP_ON_QUOTA_HIT_PERCENTAGE, Integer.class);
|
||||
}
|
||||
|
||||
protected boolean isActionsAutocloseEnabled() {
|
||||
return getConfigValue(REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class);
|
||||
return TenantConfigHelper.getAsSystem(REPOSITORY_ACTIONS_AUTOCLOSE_ENABLED, Boolean.class);
|
||||
}
|
||||
|
||||
private static Map<Long, List<TargetWithActionType>> convertRequest(final Collection<DeploymentRequest> deploymentRequests) {
|
||||
@@ -667,21 +633,20 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
backOffPolicy.setBackOffPeriod(Constants.TX_RT_DELAY);
|
||||
template.setBackOffPolicy(backOffPolicy);
|
||||
|
||||
final SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(Constants.TX_RT_MAX,
|
||||
Collections.singletonMap(ConcurrencyFailureException.class, true));
|
||||
final SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(
|
||||
Constants.TX_RT_MAX, Collections.singletonMap(ConcurrencyFailureException.class, true));
|
||||
template.setRetryPolicy(retryPolicy);
|
||||
|
||||
return template;
|
||||
}
|
||||
|
||||
private List<DistributionSetAssignmentResult> assignDistributionSets(
|
||||
final String initiatedBy, final List<DeploymentRequest> deploymentRequests, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy strategy) {
|
||||
final List<DeploymentRequest> deploymentRequests, final String actionMessage, final AbstractDsAssignmentStrategy strategy) {
|
||||
final List<DeploymentRequest> validatedRequests = validateAndFilterRequestForAssignments(deploymentRequests);
|
||||
final Map<Long, List<TargetWithActionType>> assignmentsByDsIds = convertRequest(validatedRequests);
|
||||
|
||||
final List<DistributionSetAssignmentResult> results = assignmentsByDsIds.entrySet().stream()
|
||||
.map(entry -> assignDistributionSetToTargetsWithRetry(initiatedBy, entry.getKey(), entry.getValue(), actionMessage, strategy))
|
||||
.map(entry -> assignDistributionSetToTargetsWithRetry(entry.getKey(), entry.getValue(), actionMessage, strategy))
|
||||
.toList();
|
||||
strategy.sendDeploymentEvents(results);
|
||||
return results;
|
||||
@@ -774,10 +739,10 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargetsWithRetry(
|
||||
final String initiatedBy, final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
return retryTemplate.execute(retryContext ->
|
||||
assignDistributionSetToTargets(initiatedBy, dsId, targetsWithActionType, actionMessage, assignmentStrategy));
|
||||
assignDistributionSetToTargets(dsId, targetsWithActionType, actionMessage, assignmentStrategy));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -793,7 +758,6 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
* to {@link TargetUpdateStatus#IN_SYNC} <br/>
|
||||
* D. does not send a {@link TargetAssignDistributionSetEvent}.<br/>
|
||||
*
|
||||
* @param initiatedBy the username of the user who initiated the assignment
|
||||
* @param dsId the ID of the distribution set to assign
|
||||
* @param targetsWithActionType a list of all targets and their action type
|
||||
* @param actionMessage an optional message to be written into the action status
|
||||
@@ -803,7 +767,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
* {@link DistributionSetType}.
|
||||
*/
|
||||
private DistributionSetAssignmentResult assignDistributionSetToTargets(
|
||||
final String initiatedBy, final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final Long dsId, final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
final JpaDistributionSet dsValidAndComplete = distributionSetManagement.getValidAndComplete(dsId);
|
||||
final JpaDistributionSet distributionSet;
|
||||
@@ -837,7 +801,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
.filter(target -> existingTargetIds.contains(target.getControllerId())).toList();
|
||||
|
||||
final List<JpaAction> assignedActions = doAssignDistributionSetToTargets(
|
||||
initiatedBy, existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSet, targetEntities);
|
||||
existingTargetsWithActionType, actionMessage, assignmentStrategy, distributionSet, targetEntities);
|
||||
return buildAssignmentResult(distributionSet, assignedActions, existingTargetsWithActionType.size());
|
||||
}
|
||||
|
||||
@@ -849,7 +813,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
return new DistributionSetAssignmentResult(distributionSetEntity, alreadyAssignedCount, Collections.emptyList());
|
||||
}
|
||||
|
||||
private List<JpaAction> doAssignDistributionSetToTargets(final String initiatedBy,
|
||||
private List<JpaAction> doAssignDistributionSetToTargets(
|
||||
final Collection<TargetWithActionType> targetsWithActionType, final String actionMessage,
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy, final JpaDistributionSet distributionSetEntity,
|
||||
final List<JpaTarget> targetEntities) {
|
||||
@@ -863,7 +827,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
targetEntitiesIdsChunks.forEach(this::cancelInactiveScheduledActionsForTargets);
|
||||
setAssignedDistributionSetAndTargetUpdateStatus(assignmentStrategy, distributionSetEntity, targetEntitiesIdsChunks);
|
||||
final Map<TargetWithActionType, JpaAction> assignedActions =
|
||||
createActions(targetsWithActionType, targetEntities, distributionSetEntity, assignmentStrategy, initiatedBy);
|
||||
createActions(targetsWithActionType, targetEntities, distributionSetEntity, assignmentStrategy);
|
||||
// create initial action status when action is created, so we remember
|
||||
// the initial running status because we will change the status
|
||||
// of the action itself and with this action status we have a nicer action history.
|
||||
@@ -888,7 +852,7 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
private void checkMaxAssignmentQuota(final String controllerId, final long requested) {
|
||||
final int quota = quotaManagement.getMaxActionsPerTarget();
|
||||
try {
|
||||
systemSecurityContext.runAsSystem(() -> QuotaHelper.assertAssignmentQuota(
|
||||
asSystem(() -> QuotaHelper.assertAssignmentQuota(
|
||||
controllerId, requested, quota, Action.class, Target.class, actionRepository::countByTargetControllerId));
|
||||
} catch (final AssignmentQuotaExceededException ex) {
|
||||
targetRepository.findByControllerId(controllerId).ifPresentOrElse(
|
||||
@@ -908,18 +872,18 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
}
|
||||
|
||||
private void setAssignedDistributionSetAndTargetUpdateStatus(final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
private void setAssignedDistributionSetAndTargetUpdateStatus(
|
||||
final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final JpaDistributionSet set, final List<List<Long>> targetIdsChunks) {
|
||||
final String currentUser = auditorProvider.getCurrentAuditor().orElse(null);
|
||||
assignmentStrategy.setAssignedDistributionSetAndTargetStatus(set, targetIdsChunks, currentUser);
|
||||
assignmentStrategy.setAssignedDistributionSetAndTargetStatus(set, targetIdsChunks);
|
||||
}
|
||||
|
||||
private Map<TargetWithActionType, JpaAction> createActions(final Collection<TargetWithActionType> targetsWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set, final AbstractDsAssignmentStrategy assignmentStrategy,
|
||||
final String initiatedBy) {
|
||||
private Map<TargetWithActionType, JpaAction> createActions(
|
||||
final Collection<TargetWithActionType> targetsWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set, final AbstractDsAssignmentStrategy assignmentStrategy) {
|
||||
final Map<TargetWithActionType, JpaAction> persistedActions = new LinkedHashMap<>();
|
||||
for (final TargetWithActionType twt : targetsWithActionType) {
|
||||
final JpaAction targetAction = assignmentStrategy.createTargetAction(initiatedBy, twt, targets, set);
|
||||
final JpaAction targetAction = assignmentStrategy.createTargetAction(twt, targets, set);
|
||||
if (targetAction != null) {
|
||||
persistedActions.put(twt, actionRepository.save(targetAction));
|
||||
}
|
||||
@@ -1054,15 +1018,11 @@ public class JpaDeploymentManagement extends JpaActionManagement implements Depl
|
||||
}
|
||||
|
||||
private boolean isMultiAssignmentsEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isMultiAssignmentsEnabled();
|
||||
return TenantConfigHelper.isMultiAssignmentsEnabled();
|
||||
}
|
||||
|
||||
private boolean isConfirmationFlowEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isConfirmationFlowEnabled();
|
||||
}
|
||||
|
||||
private <T extends Serializable> T getConfigValue(final String key, final Class<T> valueType) {
|
||||
return systemSecurityContext.runAsSystem(() -> tenantConfigurationManagement.getConfigurationValue(key, valueType).getValue());
|
||||
return TenantConfigHelper.isUserConfirmationFlowEnabled();
|
||||
}
|
||||
|
||||
private void assertTargetReadAllowed(final Long targetId) {
|
||||
|
||||
@@ -9,10 +9,13 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetInvalidationManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
@@ -26,8 +29,6 @@ import org.eclipse.hawkbit.repository.model.ActionCancellationType;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetInvalidation;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -47,9 +48,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
||||
private final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
private final TenantAware tenantAware;
|
||||
private final LockRegistry lockRegistry;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
protected JpaDistributionSetInvalidationManagement(
|
||||
@@ -57,23 +56,20 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
||||
final RolloutManagement rolloutManagement, final DeploymentManagement deploymentManagement,
|
||||
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
|
||||
final PlatformTransactionManager txManager, final RepositoryProperties repositoryProperties,
|
||||
final TenantAware tenantAware, final LockRegistry lockRegistry,
|
||||
final SystemSecurityContext systemSecurityContext) {
|
||||
final LockRegistry lockRegistry) {
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||
this.txManager = txManager;
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
this.tenantAware = tenantAware;
|
||||
this.lockRegistry = lockRegistry;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateDistributionSet(final DistributionSetInvalidation distributionSetInvalidation) {
|
||||
log.debug("Invalidate distribution sets {}", distributionSetInvalidation.getDistributionSetIds());
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
final String tenant = AccessContext.tenant();
|
||||
if (shouldRolloutsBeCanceled(distributionSetInvalidation.getActionCancellationType())) {
|
||||
final String handlerId = JpaRolloutManagement.createRolloutLockKey(tenant);
|
||||
final Lock lock = lockRegistry.obtain(handlerId);
|
||||
@@ -131,7 +127,7 @@ public class JpaDistributionSetInvalidationManagement implements DistributionSet
|
||||
}
|
||||
|
||||
// Do run as system to ensure all actions (even invisible) are canceled due to invalidation.
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
asSystem(() -> {
|
||||
log.debug("Cancel auto assignments after ds invalidation. ID: {}", setId);
|
||||
targetFilterQueryManagement.cancelAutoAssignmentForDistributionSet(setId);
|
||||
});
|
||||
|
||||
@@ -34,12 +34,12 @@ import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.DeletedException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSet;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
@@ -56,8 +56,6 @@ import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.Statistic;
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetFields;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
@@ -83,7 +81,6 @@ public class JpaDistributionSetManagement
|
||||
private final DistributionSetTagRepository distributionSetTagRepository;
|
||||
private final TargetFilterQueryRepository targetFilterQueryRepository;
|
||||
private final QuotaManagement quotaManagement;
|
||||
private final TenantConfigHelper tenantConfigHelper;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
@@ -95,8 +92,6 @@ public class JpaDistributionSetManagement
|
||||
final DistributionSetTagRepository distributionSetTagRepository,
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository,
|
||||
final QuotaManagement quotaManagement,
|
||||
final SystemSecurityContext systemSecurityContext,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final RepositoryProperties repositoryProperties) {
|
||||
super(jpaRepository, entityManager);
|
||||
this.distributionSetTagManagement = distributionSetTagManagement;
|
||||
@@ -104,7 +99,6 @@ public class JpaDistributionSetManagement
|
||||
this.distributionSetTagRepository = distributionSetTagRepository;
|
||||
this.targetFilterQueryRepository = targetFilterQueryRepository;
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.tenantConfigHelper = TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement);
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
}
|
||||
|
||||
@@ -209,7 +203,7 @@ public class JpaDistributionSetManagement
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Boolean.FALSE.equals(tenantConfigHelper.getConfigValue(IMPLICIT_LOCK_ENABLED, Boolean.class))) {
|
||||
if (Boolean.FALSE.equals(TenantConfigHelper.getAsSystem(IMPLICIT_LOCK_ENABLED, Boolean.class))) {
|
||||
// implicit lock disabled
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -11,10 +11,10 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetTagFields;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetTagFields;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetTypeFields;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
@@ -36,6 +35,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.DistributionSetTypeSpec
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.qfields.DistributionSetTypeFields;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
@@ -71,18 +71,16 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
private final ActionRepository actionRepository;
|
||||
private final TargetRepository targetRepository;
|
||||
private final EntityManager entityManager;
|
||||
private final RolloutStatusCache rolloutStatusCache;
|
||||
|
||||
@SuppressWarnings("java:S107")
|
||||
protected JpaRolloutGroupManagement(final RolloutGroupRepository rolloutGroupRepository,
|
||||
final RolloutRepository rolloutRepository, final ActionRepository actionRepository,
|
||||
final TargetRepository targetRepository, final EntityManager entityManager, final RolloutStatusCache rolloutStatusCache) {
|
||||
final TargetRepository targetRepository, final EntityManager entityManager) {
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.rolloutRepository = rolloutRepository;
|
||||
this.actionRepository = actionRepository;
|
||||
this.targetRepository = targetRepository;
|
||||
this.entityManager = entityManager;
|
||||
this.rolloutStatusCache = rolloutStatusCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -118,10 +116,10 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
final JpaRolloutGroup jpaRolloutGroup = (JpaRolloutGroup) rolloutGroupRepository.findById(rolloutGroupId).map(RolloutGroup.class::cast)
|
||||
.orElseThrow(() -> new EntityNotFoundException(RolloutGroup.class, rolloutGroupId));
|
||||
|
||||
List<TotalTargetCountActionStatus> rolloutStatusCountItems = rolloutStatusCache.getRolloutGroupStatus(rolloutGroupId);
|
||||
List<TotalTargetCountActionStatus> rolloutStatusCountItems = RolloutStatusCache.getRolloutGroupStatus(rolloutGroupId);
|
||||
if (CollectionUtils.isEmpty(rolloutStatusCountItems)) {
|
||||
rolloutStatusCountItems = actionRepository.getStatusCountByRolloutGroupId(rolloutGroupId);
|
||||
rolloutStatusCache.putRolloutGroupStatus(rolloutGroupId, rolloutStatusCountItems);
|
||||
RolloutStatusCache.putRolloutGroupStatus(rolloutGroupId, rolloutStatusCountItems);
|
||||
}
|
||||
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
@@ -225,7 +223,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
}
|
||||
|
||||
private Map<Long, List<TotalTargetCountActionStatus>> getStatusCountItemForRolloutGroup(final List<Long> groupIds) {
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromCache = rolloutStatusCache
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromCache = RolloutStatusCache
|
||||
.getRolloutGroupStatus(groupIds);
|
||||
|
||||
final List<Long> rolloutGroupIds = groupIds.stream().filter(id -> !fromCache.containsKey(id)).toList();
|
||||
@@ -235,7 +233,7 @@ public class JpaRolloutGroupManagement implements RolloutGroupManagement {
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromDb = resultList.stream()
|
||||
.collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
|
||||
|
||||
rolloutStatusCache.putRolloutGroupStatus(fromDb);
|
||||
RolloutStatusCache.putRolloutGroupStatus(fromDb);
|
||||
|
||||
fromCache.putAll(fromDb);
|
||||
}
|
||||
|
||||
@@ -29,9 +29,9 @@ import jakarta.validation.ValidationException;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.im.authentication.SpRole;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpRole;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
@@ -42,7 +42,6 @@ import org.eclipse.hawkbit.repository.RolloutHelper;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.RolloutStatusCache;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.RolloutGroupCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
@@ -51,6 +50,7 @@ import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetExcepti
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RolloutIllegalStateException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.Jpa;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
@@ -85,10 +85,7 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountActionStatus;
|
||||
import org.eclipse.hawkbit.repository.model.TotalTargetCountStatus;
|
||||
import org.eclipse.hawkbit.repository.qfields.RolloutFields;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.utils.ObjectCopyUtil;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
@@ -129,17 +126,13 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
private final RolloutApprovalStrategy rolloutApprovalStrategy;
|
||||
private final StartNextGroupRolloutGroupSuccessAction startNextRolloutGroupAction;
|
||||
private final RolloutStatusCache rolloutStatusCache;
|
||||
private final ActionRepository actionRepository;
|
||||
private final TargetManagement<? extends Target> targetManagement;
|
||||
private final ActionStatusRepository actionStatusRepository;
|
||||
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
|
||||
private final TenantAware tenantAware;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final QuotaManagement quotaManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final ContextAware contextAware;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
|
||||
private final OnlineDsAssignmentStrategy onlineDsAssignmentStrategy;
|
||||
|
||||
protected JpaRolloutManagement(
|
||||
@@ -148,35 +141,26 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
final RolloutGroupRepository rolloutGroupRepository,
|
||||
final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final StartNextGroupRolloutGroupSuccessAction startNextRolloutGroupAction,
|
||||
final RolloutStatusCache rolloutStatusCache,
|
||||
final ActionRepository actionRepository,
|
||||
final ActionStatusRepository actionStatusRepository,
|
||||
final TargetRepository targetRepository,
|
||||
final TargetManagement<? extends Target> targetManagement,
|
||||
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
|
||||
final TenantAware tenantAware,
|
||||
final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final QuotaManagement quotaManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware,
|
||||
final RepositoryProperties repositoryProperties) {
|
||||
this.entityManager = entityManager;
|
||||
this.rolloutRepository = rolloutRepository;
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
|
||||
this.startNextRolloutGroupAction = startNextRolloutGroupAction;
|
||||
this.rolloutStatusCache = rolloutStatusCache;
|
||||
this.actionRepository = actionRepository;
|
||||
this.actionStatusRepository = actionStatusRepository;
|
||||
this.targetManagement = targetManagement;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.tenantAware = tenantAware;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.contextAware = contextAware;
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
|
||||
this.onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
|
||||
onlineDsAssignmentStrategy = new OnlineDsAssignmentStrategy(targetRepository, actionRepository, actionStatusRepository,
|
||||
quotaManagement, this::isMultiAssignmentsEnabled, this::isConfirmationFlowEnabled, repositoryProperties, null);
|
||||
}
|
||||
|
||||
@@ -223,20 +207,21 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
} else {
|
||||
RolloutHelper.verifyRolloutGroupAmount(amountGroup, quotaManagement);
|
||||
}
|
||||
|
||||
final JpaRollout rolloutRequest = new JpaRollout();
|
||||
ObjectCopyUtil.copy(rollout, rolloutRequest, false, UnaryOperator.identity());
|
||||
rolloutRequest.setDynamic(rollout.isDynamic()); // TODO - copy compares isDynamic == false and don't set it to false - remain null
|
||||
// scheduled rollout, the creator shall have permissions to start rollout
|
||||
if (rolloutRequest.getStartAt() != null && rolloutRequest.getStartAt() != Long.MAX_VALUE && // if scheduled rollout
|
||||
!systemSecurityContext.hasPermission(SpPermission.HANDLE_ROLLOUT) &&
|
||||
!systemSecurityContext.hasPermission(SpRole.SYSTEM_ROLE)) {
|
||||
throw new InsufficientPermissionException("You need permission to start rollouts to create a scheduled rollout");
|
||||
}
|
||||
if (dynamicRolloutGroupTemplate != null && !rolloutRequest.isDynamic()) {
|
||||
if (dynamicRolloutGroupTemplate != null && !rollout.isDynamic()) {
|
||||
throw new ValidationException("Dynamic group template is only allowed for dynamic rollouts");
|
||||
}
|
||||
|
||||
// scheduled rollout, the creator shall have permissions to start rollout
|
||||
if (rollout.getStartAt() != null && rollout.getStartAt() != Long.MAX_VALUE && // if scheduled rollout
|
||||
!SpPermission.hasPermission(SpPermission.HANDLE_ROLLOUT) &&
|
||||
!SpPermission.hasPermission(SpRole.SYSTEM_ROLE)) {
|
||||
throw new InsufficientPermissionException("You need permission to start rollouts to create a scheduled rollout");
|
||||
}
|
||||
|
||||
final JpaRollout rolloutRequest = new JpaRollout();
|
||||
ObjectCopyUtil.copy(rollout, rolloutRequest, false, UnaryOperator.identity());
|
||||
// TODO - copy compares isDynamic == false and don't set it to false - remain null
|
||||
rolloutRequest.setDynamic(rollout.isDynamic());
|
||||
return createRolloutGroups(
|
||||
amountGroup, conditions, createRollout(rolloutRequest, amountGroup == 0), confirmationRequired, dynamicRolloutGroupTemplate);
|
||||
}
|
||||
@@ -318,11 +303,11 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
final Rollout rollout = rolloutRepository.findById(rolloutId).map(Rollout.class::cast)
|
||||
.orElseThrow(() -> new EntityNotFoundException(Rollout.class, rolloutId));
|
||||
|
||||
List<TotalTargetCountActionStatus> rolloutStatusCountItems = rolloutStatusCache.getRolloutStatus(rolloutId);
|
||||
List<TotalTargetCountActionStatus> rolloutStatusCountItems = RolloutStatusCache.getRolloutStatus(rolloutId);
|
||||
|
||||
if (CollectionUtils.isEmpty(rolloutStatusCountItems)) {
|
||||
rolloutStatusCountItems = actionRepository.getStatusCountByRolloutId(rolloutId);
|
||||
rolloutStatusCache.putRolloutStatus(rolloutId, rolloutStatusCountItems);
|
||||
RolloutStatusCache.putRolloutStatus(rolloutId, rolloutStatusCountItems);
|
||||
}
|
||||
|
||||
final TotalTargetCountStatus totalTargetCountStatus = new TotalTargetCountStatus(
|
||||
@@ -520,8 +505,8 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
private void softCancelActionsOfRollout(final Rollout rollout) {
|
||||
final List<JpaAction> actions = actionRepository.findAll(
|
||||
ActionSpecifications
|
||||
.byRolloutIdAndActiveAndStatusIsNot(rollout.getId(),
|
||||
List.of(Action.Status.CANCELING)), // avoid cancelling state here, because it is count as still active
|
||||
// avoid cancelling state here, because it is count as still active
|
||||
.byRolloutIdAndActiveAndStatusIsNot(rollout.getId(), List.of(Action.Status.CANCELING)),
|
||||
Pageable.ofSize(maxActions))
|
||||
.getContent();
|
||||
log.info("Found {} active actions for rollout {}, performing soft cancel.", actions.size(), rollout.getId());
|
||||
@@ -529,7 +514,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
storeActionsAndStatuses(actions, Action.Status.CANCELING);
|
||||
|
||||
// send cancellation messages to event publisher
|
||||
onlineDsAssignmentStrategy.sendCancellationMessages(actions, tenantAware.getCurrentTenant());
|
||||
onlineDsAssignmentStrategy.sendCancellationMessages(actions, AccessContext.tenant());
|
||||
}
|
||||
|
||||
private void forceQuitActionsOfRollout(final Rollout rollout) {
|
||||
@@ -592,7 +577,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
Jpa.setNativeQueryInParameter(updateQuery, "tid", targetIds);
|
||||
final int updated = updateQuery.executeUpdate();
|
||||
log.info("{} of target assigned distribution values updated for tenant {}",
|
||||
updated, tenantAware.getCurrentTenant());
|
||||
updated, AccessContext.tenant());
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -607,7 +592,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
Jpa.setNativeQueryInParameter(updateQuery, "tid", targetIds);
|
||||
final int updated = updateQuery.executeUpdate();
|
||||
log.info("{} of target assigned distribution set to previously installed distribution value for tenant {}",
|
||||
updated, tenantAware.getCurrentTenant());
|
||||
updated, AccessContext.tenant());
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -693,7 +678,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
}
|
||||
|
||||
private JpaRollout createRollout(final JpaRollout rollout, final boolean pureDynamic) {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(rollout);
|
||||
WeightValidationHelper.validate(rollout);
|
||||
|
||||
rollout.setCreatedAt(System.currentTimeMillis());
|
||||
|
||||
@@ -725,7 +710,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
if (rollout.getWeight().isEmpty()) {
|
||||
rollout.setWeight(repositoryProperties.getActionWeightIfAbsent());
|
||||
}
|
||||
contextAware.getCurrentContext().ifPresent(rollout::setAccessControlContext);
|
||||
AccessContext.securityContext().ifPresent(rollout::setAccessControlContext);
|
||||
return rollout;
|
||||
}
|
||||
|
||||
@@ -747,8 +732,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
JpaRolloutGroup lastGroup = null;
|
||||
if (amountOfGroups == 0) {
|
||||
if (dynamicRolloutGroupTemplate == null) {
|
||||
throw new ConstraintDeclarationException(
|
||||
"At least one static rollout group must be defined for a static rollout");
|
||||
throw new ConstraintDeclarationException("At least one static rollout group must be defined for a static rollout");
|
||||
}
|
||||
} else {
|
||||
// we can enforce the 'max targets per group' quota right here because
|
||||
@@ -890,7 +874,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromCache = rolloutStatusCache.getRolloutStatus(rollouts);
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromCache = RolloutStatusCache.getRolloutStatus(rollouts);
|
||||
|
||||
final List<Long> rolloutIds = rollouts.stream().filter(id -> !fromCache.containsKey(id)).toList();
|
||||
if (!rolloutIds.isEmpty()) {
|
||||
@@ -898,7 +882,7 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
final Map<Long, List<TotalTargetCountActionStatus>> fromDb = resultList.stream()
|
||||
.collect(Collectors.groupingBy(TotalTargetCountActionStatus::getId));
|
||||
|
||||
rolloutStatusCache.putRolloutStatus(fromDb);
|
||||
RolloutStatusCache.putRolloutStatus(fromDb);
|
||||
|
||||
fromCache.putAll(fromDb);
|
||||
}
|
||||
@@ -1012,11 +996,11 @@ public class JpaRolloutManagement implements RolloutManagement {
|
||||
}
|
||||
|
||||
private boolean isMultiAssignmentsEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isMultiAssignmentsEnabled();
|
||||
return TenantConfigHelper.isMultiAssignmentsEnabled();
|
||||
}
|
||||
|
||||
private boolean isConfirmationFlowEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isConfirmationFlowEnabled();
|
||||
return TenantConfigHelper.isUserConfirmationFlowEnabled();
|
||||
}
|
||||
|
||||
private record TargetCount(long total, String filter) {}
|
||||
|
||||
@@ -26,7 +26,6 @@ import jakarta.persistence.EntityManager;
|
||||
import org.eclipse.hawkbit.artifact.encryption.ArtifactEncryptionService;
|
||||
import org.eclipse.hawkbit.repository.ArtifactManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteSoftwareModuleException;
|
||||
@@ -42,6 +41,7 @@ import org.eclipse.hawkbit.repository.model.Artifact;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule.MetadataValue;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleFields;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.data.domain.Page;
|
||||
|
||||
@@ -14,12 +14,12 @@ import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
|
||||
import org.eclipse.hawkbit.repository.SoftwareModuleTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaSoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.DistributionSetTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.SoftwareModuleTypeRepository;
|
||||
import org.eclipse.hawkbit.repository.qfields.SoftwareModuleTypeFields;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
@@ -16,9 +18,9 @@ import jakarta.persistence.EntityManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.artifact.ArtifactStorage;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantStatsManagement;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.CurrentTenantCacheKeyGenerator;
|
||||
@@ -43,10 +45,6 @@ import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
import org.eclipse.hawkbit.repository.model.report.SystemUsageReport;
|
||||
import org.eclipse.hawkbit.repository.model.report.SystemUsageReportWithTenants;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager.CacheEvictEvent;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
@@ -57,7 +55,6 @@ import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.orm.jpa.vendor.Database;
|
||||
import org.springframework.retry.annotation.Backoff;
|
||||
import org.springframework.retry.annotation.Retryable;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -78,9 +75,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
|
||||
private static final int MAX_TENANTS_QUERY = 1000;
|
||||
|
||||
private final String countArtifactQuery;
|
||||
private final String countSoftwareModulesQuery;
|
||||
|
||||
private final TargetRepository targetRepository;
|
||||
private final TargetTypeRepository targetTypeRepository;
|
||||
private final TargetTagRepository targetTagRepository;
|
||||
@@ -93,10 +87,7 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
private final RolloutRepository rolloutRepository;
|
||||
private final TenantConfigurationRepository tenantConfigurationRepository;
|
||||
private final TenantMetaDataRepository tenantMetaDataRepository;
|
||||
private final TenantStatsManagement systemStatsManagement;
|
||||
private final SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final TenantAware tenantAware;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final EntityManager entityManager;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
@@ -112,8 +103,8 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
final DistributionSetRepository distributionSetRepository, final DistributionSetTypeRepository distributionSetTypeRepository,
|
||||
final DistributionSetTagRepository distributionSetTagRepository, final RolloutRepository rolloutRepository,
|
||||
final TenantConfigurationRepository tenantConfigurationRepository, final TenantMetaDataRepository tenantMetaDataRepository,
|
||||
final TenantStatsManagement systemStatsManagement, final SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator,
|
||||
final SystemSecurityContext systemSecurityContext, final TenantAware tenantAware, final PlatformTransactionManager txManager,
|
||||
final SystemManagementCacheKeyGenerator currentTenantCacheKeyGenerator,
|
||||
final PlatformTransactionManager txManager,
|
||||
final EntityManager entityManager, final RepositoryProperties repositoryProperties,
|
||||
final JpaProperties properties) {
|
||||
this.targetRepository = targetRepository;
|
||||
@@ -128,17 +119,10 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
this.rolloutRepository = rolloutRepository;
|
||||
this.tenantConfigurationRepository = tenantConfigurationRepository;
|
||||
this.tenantMetaDataRepository = tenantMetaDataRepository;
|
||||
this.systemStatsManagement = systemStatsManagement;
|
||||
this.currentTenantCacheKeyGenerator = currentTenantCacheKeyGenerator;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantAware = tenantAware;
|
||||
this.txManager = txManager;
|
||||
this.entityManager = entityManager;
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
|
||||
final String isDeleted = isPostgreSql(properties) ? "false" : "0";
|
||||
countArtifactQuery = "SELECT COUNT(a.id) FROM sp_artifact a INNER JOIN sp_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " + isDeleted;
|
||||
countSoftwareModulesQuery = "select SUM(file_size) from sp_artifact a INNER JOIN sp_software_module sm ON a.software_module = sm.id WHERE sm.deleted = " + isDeleted;
|
||||
}
|
||||
|
||||
@Autowired(required = false) // it's not required on dmf/ddi only instances
|
||||
@@ -152,34 +136,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
return currentTenantCacheKeyGenerator.currentTenantKeyGenerator();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteTenant(final String t) {
|
||||
if (artifactStorage == null) {
|
||||
throw new IllegalStateException("Artifact repository is not available. Can't delete tenant.");
|
||||
}
|
||||
|
||||
final String tenant = t.toUpperCase();
|
||||
tenantAware.runAsTenant(tenant, () -> DeploymentHelper.runInNewTransaction(txManager, "deleteTenant", status -> {
|
||||
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
|
||||
tenantConfigurationRepository.deleteByTenant(tenant);
|
||||
targetRepository.deleteByTenant(tenant);
|
||||
targetFilterQueryRepository.deleteByTenant(tenant);
|
||||
rolloutRepository.deleteByTenant(tenant);
|
||||
targetTypeRepository.deleteByTenant(tenant);
|
||||
targetTagRepository.deleteByTenant(tenant);
|
||||
distributionSetTagRepository.deleteByTenant(tenant);
|
||||
distributionSetRepository.deleteByTenant(tenant);
|
||||
distributionSetTypeRepository.deleteByTenant(tenant);
|
||||
softwareModuleRepository.deleteByTenant(tenant);
|
||||
artifactStorage.deleteByTenant(tenant);
|
||||
softwareModuleTypeRepository.deleteByTenant(tenant);
|
||||
return null;
|
||||
}));
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new CacheEvictEvent.Default(tenant, null, null));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Page<String> findTenants(final Pageable pageable) {
|
||||
return tenantMetaDataRepository.findTenants(pageable);
|
||||
@@ -190,51 +146,21 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
// Exception squid:S2229 - calling findTenants without transaction is intended in this case
|
||||
@SuppressWarnings("squid:S2229")
|
||||
public void forEachTenant(final Consumer<String> consumer) {
|
||||
forEachTenant0(consumer);
|
||||
}
|
||||
|
||||
private void forEachTenant0(final Consumer<String> consumer) {
|
||||
Page<String> tenants;
|
||||
Pageable query = PageRequest.of(0, MAX_TENANTS_QUERY);
|
||||
do {
|
||||
tenants = findTenants(query);
|
||||
tenants.forEach(tenant -> tenantAware.runAsTenant(tenant, () -> {
|
||||
tenants = findTenants(query); // with IS_SYSTEM_CODE so we could find all tenants
|
||||
tenants.forEach(tenant -> asSystemAsTenant(tenant, () -> {
|
||||
try {
|
||||
consumer.accept(tenant);
|
||||
} catch (final RuntimeException ex) {
|
||||
log.debug("Exception on forEachTenant execution for tenant {}. Continue with next tenant.",
|
||||
tenant, ex);
|
||||
log.error("Exception on forEachTenant execution for tenant {} with error message [{}]. "
|
||||
+ "Continue with next tenant.", tenant, ex.getMessage());
|
||||
log.error("Exception on forEachTenant execution for tenant {} with error message [{}]. Continue with next tenant.",
|
||||
tenant, ex.getMessage());
|
||||
}
|
||||
return null;
|
||||
}));
|
||||
} while ((query = tenants.nextPageable()) != Pageable.unpaged());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUsageReportWithTenants getSystemUsageStatisticsWithTenants() {
|
||||
final SystemUsageReportWithTenants result = (SystemUsageReportWithTenants) getSystemUsageStatistics();
|
||||
usageStatsPerTenant(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SystemUsageReport getSystemUsageStatistics() {
|
||||
final Number count = (Number) entityManager.createNativeQuery(countSoftwareModulesQuery).getSingleResult();
|
||||
|
||||
long sumOfArtifacts = 0;
|
||||
if (count != null) {
|
||||
sumOfArtifacts = count.longValue();
|
||||
}
|
||||
|
||||
// we use native queries to punch through the tenant boundaries. This has to be used with care!
|
||||
final long targets = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_target").getSingleResult()).longValue();
|
||||
final long artifacts = ((Number) entityManager.createNativeQuery(countArtifactQuery).getSingleResult()).longValue();
|
||||
final long actions = ((Number) entityManager.createNativeQuery("SELECT COUNT(id) FROM sp_action").getSingleResult()).longValue();
|
||||
return new SystemUsageReportWithTenants(targets, artifacts, actions, sumOfArtifacts, tenantMetaDataRepository.count());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TenantMetaData getTenantMetadata() {
|
||||
return getTenantMetadata0(true);
|
||||
@@ -265,23 +191,46 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
return tenantMetaDataRepository.save(data);
|
||||
}
|
||||
|
||||
private static boolean isPostgreSql(final JpaProperties properties) {
|
||||
return Database.POSTGRESQL == properties.getDatabase();
|
||||
@Override
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
backoff = @Backoff(delay = Constants.TX_RT_DELAY))
|
||||
public void deleteTenant(final String t) {
|
||||
if (artifactStorage == null) {
|
||||
throw new IllegalStateException("Artifact repository is not available. Can't delete tenant.");
|
||||
}
|
||||
|
||||
final String tenant = t.toUpperCase();
|
||||
asSystemAsTenant(tenant, () -> DeploymentHelper.runInNewTransaction(txManager, "deleteTenant", status -> {
|
||||
tenantMetaDataRepository.deleteByTenantIgnoreCase(tenant);
|
||||
tenantConfigurationRepository.deleteByTenant(tenant);
|
||||
targetRepository.deleteByTenant(tenant);
|
||||
targetFilterQueryRepository.deleteByTenant(tenant);
|
||||
rolloutRepository.deleteByTenant(tenant);
|
||||
targetTypeRepository.deleteByTenant(tenant);
|
||||
targetTagRepository.deleteByTenant(tenant);
|
||||
distributionSetTagRepository.deleteByTenant(tenant);
|
||||
distributionSetRepository.deleteByTenant(tenant);
|
||||
distributionSetTypeRepository.deleteByTenant(tenant);
|
||||
softwareModuleRepository.deleteByTenant(tenant);
|
||||
artifactStorage.deleteByTenant(tenant);
|
||||
softwareModuleTypeRepository.deleteByTenant(tenant);
|
||||
return null;
|
||||
}));
|
||||
EventPublisherHolder.getInstance().getEventPublisher().publishEvent(new CacheEvictEvent.Default(tenant, null, null));
|
||||
}
|
||||
|
||||
private TenantMetaData getTenantMetadata0(final boolean withDetails) {
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
final String tenant = AccessContext.tenant();
|
||||
if (tenant == null) {
|
||||
throw new IllegalStateException("Tenant not set");
|
||||
throw new IllegalStateException("AccessContext not set");
|
||||
}
|
||||
|
||||
final TenantMetaData metaData =
|
||||
withDetails ?
|
||||
tenantMetaDataRepository.findWitDetailsByTenantIgnoreCase(tenant) :
|
||||
tenantMetaDataRepository.findByTenantIgnoreCase(tenant);
|
||||
final TenantMetaData metaData = withDetails
|
||||
? tenantMetaDataRepository.findWitDetailsByTenantIgnoreCase(tenant)
|
||||
: tenantMetaDataRepository.findByTenantIgnoreCase(tenant);
|
||||
if (metaData == null) {
|
||||
if (repositoryProperties.isImplicitTenantCreateAllowed()) {
|
||||
log.info("Tenant {} doesn't exist create metadata", tenant, new Exception("Thread dump"));
|
||||
log.info("AccessContext {} doesn't exist create metadata", tenant, new Exception("Thread dump"));
|
||||
return createTenantMetadata0(tenant);
|
||||
} else {
|
||||
throw new EntityNotFoundException(TenantMetaData.class, tenant);
|
||||
@@ -291,10 +240,6 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
}
|
||||
}
|
||||
|
||||
private void usageStatsPerTenant(final SystemUsageReportWithTenants report) {
|
||||
forEachTenant0(tenant -> report.addTenantData(systemStatsManagement.getStatsOfTenant()));
|
||||
}
|
||||
|
||||
private DistributionSetType createStandardSoftwareDataSetup() {
|
||||
final SoftwareModuleType app = softwareModuleTypeRepository.save(
|
||||
new JpaSoftwareModuleType(
|
||||
@@ -338,23 +283,20 @@ public class JpaSystemManagement implements CurrentTenantCacheKeyGenerator, Syst
|
||||
}
|
||||
|
||||
/**
|
||||
* Creating the initial tenant meta-data in a new transaction. Due to the
|
||||
* {@link org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager} is using the current tenant to
|
||||
* set the necessary tenant discriminator to the query. This is not working
|
||||
* if we don't have a current tenant set. Due to the
|
||||
* {@link #createTenantMetadata(String)} is maybe called without having a
|
||||
* current tenant we need to re-open a new transaction so the
|
||||
* {@link org.eclipse.hawkbit.repository.jpa.configuration.MultiTenantJpaTransactionManager} is called again and set the
|
||||
* Creating the initial tenant meta-data in a new transaction. Due to the tenant support it is using the current tenant to
|
||||
* set the necessary tenant discriminator to the query. This is not working if we don't have a current tenant set.
|
||||
* Due to the {@link #createTenantMetadata(String)} is maybe called without having a
|
||||
* current tenant we need to re-open a new transaction so the tenant support is called again and set the
|
||||
* tenant for this transaction.
|
||||
*
|
||||
* @param tenant the tenant to be created
|
||||
* @return the initial created {@link TenantMetaData}
|
||||
*/
|
||||
private TenantMetaData createInitialTenantMetaData(final String tenant) {
|
||||
return systemSecurityContext.runAsSystemAsTenant(
|
||||
() -> DeploymentHelper.runInNewTransaction(txManager, "initial-tenant-creation", status -> {
|
||||
return asSystemAsTenant(
|
||||
tenant, () -> DeploymentHelper.runInNewTransaction(txManager, "initial-tenant-creation", status -> {
|
||||
final DistributionSetType defaultDsType = createStandardSoftwareDataSetup();
|
||||
return tenantMetaDataRepository.save(new JpaTenantMetaData(defaultDsType, tenant));
|
||||
}), tenant);
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,19 +18,19 @@ import jakarta.persistence.EntityManager;
|
||||
|
||||
import cz.jirutka.rsql.parser.RSQLParserException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.DistributionSetManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.IncompleteDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidAutoAssignActionTypeException;
|
||||
import org.eclipse.hawkbit.repository.exception.InvalidDistributionSetException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetFilterQuery;
|
||||
@@ -44,10 +44,7 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetFilterQuery;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFilterQueryFields;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.data.domain.AuditorAware;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
@@ -66,34 +63,26 @@ import org.springframework.validation.annotation.Validated;
|
||||
@Service
|
||||
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "target-filter-management" }, matchIfMissing = true)
|
||||
class JpaTargetFilterQueryManagement
|
||||
extends AbstractJpaRepositoryManagement<JpaTargetFilterQuery, TargetFilterQueryManagement.Create, TargetFilterQueryManagement.Update, TargetFilterQueryRepository, TargetFilterQueryFields>
|
||||
extends
|
||||
AbstractJpaRepositoryManagement<JpaTargetFilterQuery, TargetFilterQueryManagement.Create, TargetFilterQueryManagement.Update, TargetFilterQueryRepository, TargetFilterQueryFields>
|
||||
implements TargetFilterQueryManagement<JpaTargetFilterQuery> {
|
||||
|
||||
private final TargetManagement<? extends Target> targetManagement;
|
||||
private final DistributionSetManagement<? extends DistributionSet> distributionSetManagement;
|
||||
private final QuotaManagement quotaManagement;
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final ContextAware contextAware;
|
||||
private final AuditorAware<String> auditorAware;
|
||||
|
||||
protected JpaTargetFilterQueryManagement(
|
||||
final TargetFilterQueryRepository targetFilterQueryRepository, final EntityManager entityManager,
|
||||
final TargetManagement<? extends Target> targetManagement,
|
||||
final DistributionSetManagement<? extends DistributionSet> distributionSetManagement,
|
||||
final QuotaManagement quotaManagement, final TenantConfigurationManagement tenantConfigurationManagement,
|
||||
final RepositoryProperties repositoryProperties,
|
||||
final SystemSecurityContext systemSecurityContext, final ContextAware contextAware, final AuditorAware<String> auditorAware) {
|
||||
final QuotaManagement quotaManagement,
|
||||
final RepositoryProperties repositoryProperties) {
|
||||
super(targetFilterQueryRepository, entityManager);
|
||||
this.targetManagement = targetManagement;
|
||||
this.distributionSetManagement = distributionSetManagement;
|
||||
this.quotaManagement = quotaManagement;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.contextAware = contextAware;
|
||||
this.auditorAware = auditorAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -161,7 +150,7 @@ class JpaTargetFilterQueryManagement
|
||||
targetFilterQuery.setAutoAssignInitiatedBy(null);
|
||||
targetFilterQuery.setConfirmationRequired(false);
|
||||
} else {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(update);
|
||||
WeightValidationHelper.validate(update);
|
||||
assertMaxTargetsQuota(targetFilterQuery.getQuery(), targetFilterQuery.getName(), update.dsId());
|
||||
|
||||
DistributionSet distributionSet = distributionSetManagement.getValidAndComplete(update.dsId());
|
||||
@@ -170,12 +159,13 @@ class JpaTargetFilterQueryManagement
|
||||
}
|
||||
|
||||
targetFilterQuery.setAutoAssignDistributionSet(distributionSet);
|
||||
contextAware.getCurrentContext().ifPresent(targetFilterQuery::setAccessControlContext);
|
||||
targetFilterQuery.setAutoAssignInitiatedBy(auditorAware.getCurrentAuditor().orElse(targetFilterQuery.getCreatedBy()));
|
||||
AccessContext.securityContext().ifPresent(targetFilterQuery::setAccessControlContext);
|
||||
targetFilterQuery.setAutoAssignInitiatedBy(Optional.ofNullable(AccessContext.actor()).orElse(targetFilterQuery.getCreatedBy()));
|
||||
targetFilterQuery.setAutoAssignActionType(sanitizeAutoAssignActionType(update.actionType()));
|
||||
targetFilterQuery.setAutoAssignWeight(update.weight() == null ? repositoryProperties.getActionWeightIfAbsent() : update.weight());
|
||||
final boolean confirmationRequired =
|
||||
update.confirmationRequired() == null ? isConfirmationFlowEnabled() : update.confirmationRequired();
|
||||
final boolean confirmationRequired = update.confirmationRequired() == null
|
||||
? TenantConfigHelper.isUserConfirmationFlowEnabled()
|
||||
: update.confirmationRequired();
|
||||
targetFilterQuery.setConfirmationRequired(confirmationRequired);
|
||||
}
|
||||
return jpaRepository.save(targetFilterQuery);
|
||||
@@ -200,10 +190,6 @@ class JpaTargetFilterQueryManagement
|
||||
return actionType;
|
||||
}
|
||||
|
||||
private boolean isConfirmationFlowEnabled() {
|
||||
return TenantConfigHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).isConfirmationFlowEnabled();
|
||||
}
|
||||
|
||||
private void assertMaxTargetsQuota(final String query, final String filterName, final long dsId) {
|
||||
QuotaHelper.assertAssignmentQuota(filterName,
|
||||
targetManagement.countByRsqlAndNonDsAndCompatibleAndUpdatable(dsId, query),
|
||||
@@ -230,7 +216,7 @@ class JpaTargetFilterQueryManagement
|
||||
|
||||
// enforce the 'max targets per auto assign' quota right here even if the result of the filter query can vary over time
|
||||
Optional.ofNullable(create.getAutoAssignDistributionSet()).ifPresent(dsId -> {
|
||||
WeightValidationHelper.usingContext(systemSecurityContext, tenantConfigurationManagement).validate(create);
|
||||
WeightValidationHelper.validate(create);
|
||||
assertMaxTargetsQuota(query, create.getName(), dsId.getId());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,10 +11,10 @@ package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetTagFields;
|
||||
import org.eclipse.hawkbit.repository.TargetTagManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetTagRepository;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetTagFields;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@@ -16,7 +16,6 @@ import java.util.function.ToLongFunction;
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
|
||||
import org.eclipse.hawkbit.repository.TargetTypeManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.AssignmentQuotaExceededException;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
@@ -31,6 +30,7 @@ import org.eclipse.hawkbit.repository.jpa.specifications.TargetTypeSpecification
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.QuotaHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetTypeFields;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import static org.eclipse.hawkbit.im.authentication.SpPermission.READ_GATEWAY_SECURITY_TOKEN;
|
||||
import static org.eclipse.hawkbit.auth.SpPermission.READ_GATEWAY_SECURITY_TOKEN;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.BATCH_ASSIGNMENTS_ENABLED;
|
||||
import static org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey.MULTI_ASSIGNMENTS_ENABLED;
|
||||
@@ -30,12 +30,14 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValidatorException;
|
||||
import org.eclipse.hawkbit.repository.exception.TenantConfigurationValueChangeNotAllowedException;
|
||||
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTarget;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTenantConfiguration;
|
||||
@@ -45,7 +47,6 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfiguration;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAwareCacheManager;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.DurationHelper;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.PollingTime;
|
||||
@@ -54,6 +55,8 @@ import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.event.ContextRefreshedEvent;
|
||||
import org.springframework.core.convert.ConversionException;
|
||||
import org.springframework.core.convert.support.ConfigurableConversionService;
|
||||
import org.springframework.core.convert.support.DefaultConversionService;
|
||||
@@ -73,7 +76,7 @@ import org.springframework.validation.annotation.Validated;
|
||||
@Validated
|
||||
@Service
|
||||
@ConditionalOnBooleanProperty(prefix = "hawkbit.jpa", name = { "enabled", "tenant-configuration-management" }, matchIfMissing = true)
|
||||
public class JpaTenantConfigurationManagement implements TenantConfigurationManagement {
|
||||
public class JpaTenantConfigurationManagement implements TenantConfigurationManagement, ApplicationListener<ContextRefreshedEvent> {
|
||||
|
||||
private static final String CACHE_TENANT_CONFIGURATION_NAME = JpaTenantConfiguration.class.getSimpleName();
|
||||
private static final ConfigurableConversionService CONVERSION_SERVICE = new DefaultConversionService();
|
||||
@@ -91,6 +94,12 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(final ContextRefreshedEvent event) {
|
||||
// Sets the proxy / bean from the context in order to be used via proxy and onore things like @PreAuthorize and @Transactional
|
||||
TenantConfigHelper.setTenantConfigurationManagement(applicationContext.getBean(JpaTenantConfigurationManagement.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
@Retryable(retryFor = { ConcurrencyFailureException.class }, maxAttempts = Constants.TX_RT_MAX,
|
||||
@@ -170,8 +179,7 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
|
||||
private void checkAccess(final String keyName) {
|
||||
if (AUTHENTICATION_GATEWAY_SECURITY_TOKEN_KEY.equalsIgnoreCase(keyName)) {
|
||||
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||
if (!SystemSecurityContext.isCurrentThreadSystemCode() && !systemSecurityContext.hasPermission(READ_GATEWAY_SECURITY_TOKEN)) {
|
||||
if (!AccessContext.isCurrentThreadSystemCode() && !SpPermission.hasPermission(READ_GATEWAY_SECURITY_TOKEN)) {
|
||||
throw new InsufficientPermissionException(
|
||||
"Can't read gateway security token! " + READ_GATEWAY_SECURITY_TOKEN + " is required!");
|
||||
}
|
||||
@@ -214,7 +222,8 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
.build()));
|
||||
}
|
||||
|
||||
private <T extends Serializable> void validateConfigurationValue(final T value, final TenantConfigurationKey configurationKey, final Object convertedValue) {
|
||||
private <T extends Serializable> void validateConfigurationValue(final T value, final TenantConfigurationKey configurationKey,
|
||||
final Object convertedValue) {
|
||||
configurationKey.validate(convertedValue, applicationContext);
|
||||
// additional validation for specific configuration keys
|
||||
if (POLLING_TIME.equals(configurationKey.getKeyName())) {
|
||||
@@ -254,7 +263,6 @@ public class JpaTenantConfigurationManagement implements TenantConfigurationMana
|
||||
return convertedValue;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends Serializable> TenantConfigurationValue<T> getConfigurationValue0(final String keyName, final Class<T> propertyType) {
|
||||
checkAccess(keyName);
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.management;
|
||||
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.TenantStatsManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ActionRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.ArtifactRepository;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.TargetRepository;
|
||||
import org.eclipse.hawkbit.repository.model.report.TenantUsage;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBooleanProperty;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Propagation;
|
||||
@@ -32,21 +32,18 @@ public class JpaTenantStatsManagement implements TenantStatsManagement {
|
||||
private final TargetRepository targetRepository;
|
||||
private final ArtifactRepository artifactRepository;
|
||||
private final ActionRepository actionRepository;
|
||||
private final TenantAware tenantAware;
|
||||
|
||||
protected JpaTenantStatsManagement(
|
||||
final TargetRepository targetRepository, final ArtifactRepository artifactRepository, final ActionRepository actionRepository,
|
||||
final TenantAware tenantAware) {
|
||||
final TargetRepository targetRepository, final ArtifactRepository artifactRepository, final ActionRepository actionRepository) {
|
||||
this.targetRepository = targetRepository;
|
||||
this.artifactRepository = artifactRepository;
|
||||
this.actionRepository = actionRepository;
|
||||
this.tenantAware = tenantAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||
public TenantUsage getStatsOfTenant() {
|
||||
final String tenant = tenantAware.getCurrentTenant();
|
||||
final String tenant = AccessContext.tenant();
|
||||
|
||||
final TenantUsage result = new TenantUsage(tenant);
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryConstants;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
@@ -22,7 +23,6 @@ import org.eclipse.hawkbit.repository.exception.InsufficientPermissionException;
|
||||
import org.eclipse.hawkbit.repository.jpa.JpaManagementHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.acm.AccessController;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.management.JpaDeploymentManagement.MaxAssignmentsExceededInfo;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaActionStatus;
|
||||
@@ -54,9 +54,9 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(initiatedBy, targetWithActionType, targets, set);
|
||||
public JpaAction createTargetAction(
|
||||
final TargetWithActionType targetWithActionType, final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(targetWithActionType, targets, set);
|
||||
if (result != null) {
|
||||
result.setStatus(Status.FINISHED);
|
||||
result.setActive(Boolean.FALSE);
|
||||
@@ -94,8 +94,7 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAssignedDistributionSetAndTargetStatus(
|
||||
final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
|
||||
public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds) {
|
||||
final long now = System.currentTimeMillis();
|
||||
targetIds.forEach(targetIdsChunk -> {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE,
|
||||
@@ -103,7 +102,7 @@ class OfflineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
throw new InsufficientPermissionException("No update access to all targets!");
|
||||
}
|
||||
targetRepository.setAssignedAndInstalledDistributionSetAndUpdateStatus(
|
||||
TargetUpdateStatus.IN_SYNC, set, now, currentUser, targetIdsChunk);
|
||||
TargetUpdateStatus.IN_SYNC, set, now, AccessContext.actor(), targetIdsChunk);
|
||||
// TODO AC - current problem with this approach is that the caller detach the targets and seems doesn't save them
|
||||
// targetRepository.saveAll(
|
||||
// targetRepository
|
||||
|
||||
@@ -19,6 +19,7 @@ import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.collections4.ListUtils;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
import org.eclipse.hawkbit.repository.RepositoryProperties;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
@@ -74,9 +75,9 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public JpaAction createTargetAction(final String initiatedBy, final TargetWithActionType targetWithActionType,
|
||||
public JpaAction createTargetAction(final TargetWithActionType targetWithActionType,
|
||||
final List<JpaTarget> targets, final JpaDistributionSet set) {
|
||||
final JpaAction result = super.createTargetAction(initiatedBy, targetWithActionType, targets, set);
|
||||
final JpaAction result = super.createTargetAction(targetWithActionType, targets, set);
|
||||
if (result != null) {
|
||||
final boolean confirmationRequired = targetWithActionType.isConfirmationRequired()
|
||||
&& result.getTarget().getAutoConfirmationStatus() == null;
|
||||
@@ -125,15 +126,15 @@ class OnlineDsAssignmentStrategy extends AbstractDsAssignmentStrategy {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAssignedDistributionSetAndTargetStatus(
|
||||
final JpaDistributionSet set, final List<List<Long>> targetIds, final String currentUser) {
|
||||
public void setAssignedDistributionSetAndTargetStatus(final JpaDistributionSet set, final List<List<Long>> targetIds) {
|
||||
final long now = System.currentTimeMillis();
|
||||
targetIds.forEach(targetIdsChunk -> {
|
||||
if (targetRepository.count(AccessController.Operation.UPDATE,
|
||||
targetRepository.byIdsSpec(targetIdsChunk)) != targetIdsChunk.size()) {
|
||||
throw new InsufficientPermissionException("No update access to all targets!");
|
||||
}
|
||||
targetRepository.setAssignedDistributionSetAndUpdateStatus(set, now, currentUser, TargetUpdateStatus.PENDING, targetIdsChunk);
|
||||
targetRepository.setAssignedDistributionSetAndUpdateStatus(
|
||||
set, now, AccessContext.actor(), TargetUpdateStatus.PENDING, targetIdsChunk);
|
||||
// TODO AC - current problem with this approach is that the caller detach the targets and seems doesn't save them
|
||||
// targetRepository.saveAll(
|
||||
// targetRepository
|
||||
|
||||
@@ -11,7 +11,6 @@ package org.eclipse.hawkbit.repository.jpa.model;
|
||||
|
||||
import java.io.Serial;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -33,7 +32,6 @@ import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeCre
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.DistributionSetTypeUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModule;
|
||||
import org.eclipse.hawkbit.repository.model.SoftwareModuleType;
|
||||
|
||||
/**
|
||||
|
||||
@@ -48,11 +48,13 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.im.authentication.SpPermission;
|
||||
import org.eclipse.hawkbit.auth.SpPermission;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.event.EventPublisherHolder;
|
||||
import org.eclipse.hawkbit.repository.event.remote.TargetDeletedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetCreatedEvent;
|
||||
import org.eclipse.hawkbit.repository.event.remote.entity.TargetUpdatedEvent;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.MapAttributeConverter;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.PollStatus;
|
||||
@@ -60,9 +62,6 @@ import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetType;
|
||||
import org.eclipse.hawkbit.repository.model.TargetUpdateStatus;
|
||||
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
@@ -215,8 +214,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
|
||||
@Override
|
||||
public String getSecurityToken() {
|
||||
final SystemSecurityContext systemSecurityContext = SystemSecurityContextHolder.getInstance().getSystemSecurityContext();
|
||||
if (SystemSecurityContext.isCurrentThreadSystemCode() || systemSecurityContext.hasPermission(SpPermission.READ_TARGET_SECURITY_TOKEN)) {
|
||||
if (AccessContext.isCurrentThreadSystemCode() || SpPermission.hasPermission(SpPermission.READ_TARGET_SECURITY_TOKEN)) {
|
||||
return securityToken;
|
||||
}
|
||||
return null;
|
||||
@@ -232,7 +230,7 @@ public class JpaTarget extends AbstractJpaNamedEntity implements Target, EventAw
|
||||
if (lastTargetQuery == null) {
|
||||
return null;
|
||||
}
|
||||
return TenantConfigurationManagementHolder.getInstance().getTenantConfigurationManagement()
|
||||
return TenantConfigHelper.getTenantConfigurationManagement()
|
||||
.pollStatusResolver()
|
||||
.apply(this);
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.eclipse.hawkbit.repository.model.TenantMetaData;
|
||||
|
||||
/**
|
||||
* Tenant entity with meta data that is configured globally for the entire
|
||||
* AccessContext entity with meta data that is configured globally for the entire
|
||||
* tenant. This entity is not tenant aware to allow the system to access it
|
||||
* through the {@link EntityManager} even before the actual tenant exists.
|
||||
*
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
||||
@@ -10,12 +10,10 @@
|
||||
package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TargetTag;
|
||||
import org.eclipse.hawkbit.repository.model.TenantAwareBaseEntity;
|
||||
import org.springframework.data.jpa.repository.Modifying;
|
||||
|
||||
@@ -9,8 +9,6 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.repository;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaTargetTag;
|
||||
|
||||
@@ -9,14 +9,14 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.rollout.condition;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.EntityNotFoundException;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaRolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
|
||||
/**
|
||||
* Error action evaluator which pauses the whole {@link Rollout} and sets the
|
||||
@@ -25,16 +25,12 @@ import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator<RolloutGroup.RolloutGroupErrorAction> {
|
||||
|
||||
private final RolloutManagement rolloutManagement;
|
||||
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
public PauseRolloutGroupAction(final RolloutManagement rolloutManagement,
|
||||
final RolloutGroupRepository rolloutGroupRepository, final SystemSecurityContext systemSecurityContext) {
|
||||
final RolloutGroupRepository rolloutGroupRepository) {
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,23 +40,19 @@ public class PauseRolloutGroupAction implements RolloutGroupActionEvaluator<Roll
|
||||
|
||||
@Override
|
||||
public void exec(final Rollout rollout, final RolloutGroup rolloutG) {
|
||||
|
||||
final JpaRolloutGroup rolloutGroup = (JpaRolloutGroup) rolloutG;
|
||||
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.ERROR);
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
/*
|
||||
Refresh latest rollout state in order to escape cases when
|
||||
previous group have matched error condition and paused the rollout
|
||||
and this one tries to pause the rollout too but throws an exception
|
||||
and rollbacks rollout processing transaction
|
||||
*/
|
||||
final Rollout refreshedRollout = rolloutManagement.get(rollout.getId());
|
||||
if (Rollout.RolloutStatus.PAUSED != refreshedRollout.getStatus()) {
|
||||
// if only the latest state is != paused then pause
|
||||
rolloutManagement.pauseRollout(rollout.getId());
|
||||
}
|
||||
});
|
||||
rolloutGroup.setStatus(RolloutGroupStatus.ERROR);
|
||||
rolloutGroupRepository.save(rolloutGroup);
|
||||
// Refresh latest rollout state in order to avoid cases when
|
||||
// previous group have matched error condition and paused the rollout
|
||||
// and this one tries to pause the rollout too but throws an exception
|
||||
// and rollbacks rollout processing transaction
|
||||
final Rollout refreshedRollout = rolloutManagement.get(rollout.getId());
|
||||
if (Rollout.RolloutStatus.PAUSED != refreshedRollout.getStatus()) {
|
||||
// if only the latest state is != paused then pause
|
||||
// execute as system if rollout creator has CREATE_ROLLOUT right but no HANDLE_ROLLOUT
|
||||
asSystem(() -> rolloutManagement.pauseRollout(rollout.getId()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.rollout.condition;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.repository.RolloutGroupRepository;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
|
||||
/**
|
||||
* Success action which starts the next following {@link RolloutGroup}.
|
||||
@@ -25,14 +26,11 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi
|
||||
|
||||
private final RolloutGroupRepository rolloutGroupRepository;
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
public StartNextGroupRolloutGroupSuccessAction(
|
||||
final RolloutGroupRepository rolloutGroupRepository, final DeploymentManagement deploymentManagement,
|
||||
final SystemSecurityContext systemSecurityContext) {
|
||||
final RolloutGroupRepository rolloutGroupRepository, final DeploymentManagement deploymentManagement) {
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -40,11 +38,12 @@ public class StartNextGroupRolloutGroupSuccessAction implements RolloutGroupActi
|
||||
return RolloutGroup.RolloutGroupSuccessAction.NEXTGROUP;
|
||||
}
|
||||
|
||||
// Note - the exec could be called by JpaRolloutsExecutor and buy JpaRolloutsManagement#triggerNextGroup
|
||||
// Note - the exec could be called by JpaRolloutsExecutor and by JpaRolloutsManagement#triggerNextGroup
|
||||
// this means it could be called by concurrently.
|
||||
@Override
|
||||
public void exec(final Rollout rollout, final RolloutGroup rolloutGroup) {
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
// as system so to assume needed permissions. When called the permission to start next group are assumed anyway
|
||||
asSystem(() -> {
|
||||
// retrieve all actions according to the parent group of the finished rolloutGroup,
|
||||
// so retrieve all child-group actions which need to be started.
|
||||
deploymentManagement.startScheduledActionsByRolloutGroupParent(
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.scheduler;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
@@ -17,7 +19,6 @@ import io.micrometer.core.instrument.MeterRegistry;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
@@ -31,24 +32,14 @@ public class AutoAssignScheduler {
|
||||
private static final String PROP_SCHEDULER_DELAY_PLACEHOLDER = "${hawkbit.scheduler.scheduler.fixedDelay:2000}";
|
||||
|
||||
private final SystemManagement systemManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final AutoAssignExecutor autoAssignExecutor;
|
||||
private final LockRegistry lockRegistry;
|
||||
private final Optional<MeterRegistry> meterRegistry;
|
||||
|
||||
/**
|
||||
* Instantiates a new AutoAssignScheduler
|
||||
*
|
||||
* @param systemManagement to find all tenants
|
||||
* @param systemSecurityContext to run as system
|
||||
* @param autoAssignExecutor to run a check as tenant
|
||||
* @param lockRegistry to acquire a lock per tenant
|
||||
*/
|
||||
public AutoAssignScheduler(final SystemManagement systemManagement,
|
||||
final SystemSecurityContext systemSecurityContext, final AutoAssignExecutor autoAssignExecutor,
|
||||
public AutoAssignScheduler(
|
||||
final SystemManagement systemManagement, final AutoAssignExecutor autoAssignExecutor,
|
||||
final LockRegistry lockRegistry, final Optional<MeterRegistry> meterRegistry) {
|
||||
this.systemManagement = systemManagement;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.autoAssignExecutor = autoAssignExecutor;
|
||||
this.lockRegistry = lockRegistry;
|
||||
this.meterRegistry = meterRegistry;
|
||||
@@ -56,13 +47,12 @@ public class AutoAssignScheduler {
|
||||
|
||||
/**
|
||||
* Scheduler method called by the spring-async mechanism. Retrieves all tenants and runs for each
|
||||
* tenant the auto assignments defined in the target filter queries {@link SystemSecurityContext}.
|
||||
* tenant the auto assignments defined in the target filter queries {@link System}.
|
||||
*/
|
||||
@Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
|
||||
public void autoAssignScheduler() {
|
||||
// run this code in system code privileged to have the necessary
|
||||
// permission to query and create entities.
|
||||
systemSecurityContext.runAsSystem(this::executeAutoAssign);
|
||||
// run this code in system code privileged to have the necessary permission to query and create entities.
|
||||
asSystem(this::executeAutoAssign);
|
||||
}
|
||||
|
||||
@SuppressWarnings("squid:S3516")
|
||||
@@ -75,11 +65,11 @@ public class AutoAssignScheduler {
|
||||
return null;
|
||||
}
|
||||
|
||||
final long startNano = System.nanoTime();
|
||||
final long startNano = java.lang.System.nanoTime();
|
||||
try {
|
||||
log.debug("Auto assign scheduled execution has acquired lock and started for each tenant.");
|
||||
systemManagement.forEachTenant(tenant -> {
|
||||
final long startNanoT = System.nanoTime();
|
||||
final long startNanoT = java.lang.System.nanoTime();
|
||||
|
||||
autoAssignExecutor.checkAllTargets();
|
||||
|
||||
@@ -87,13 +77,13 @@ public class AutoAssignScheduler {
|
||||
.map(mReg -> mReg.timer(
|
||||
"hawkbit.scheduler.executor",
|
||||
DefaultTenantConfiguration.TENANT_TAG, tenant))
|
||||
.ifPresent(timer -> timer.record(System.nanoTime() - startNanoT, TimeUnit.NANOSECONDS));
|
||||
.ifPresent(timer -> timer.record(java.lang.System.nanoTime() - startNanoT, TimeUnit.NANOSECONDS));
|
||||
});
|
||||
} finally {
|
||||
lock.unlock();
|
||||
meterRegistry
|
||||
.map(mReg -> mReg.timer("hawkbit.scheduler.executor.all"))
|
||||
.ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
.ifPresent(timer -> timer.record(java.lang.System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
log.debug("Auto assign scheduled execution has released lock and finished.");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.scheduler;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asActor;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.withSecurityContext;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@@ -16,12 +19,11 @@ import java.util.function.Consumer;
|
||||
import jakarta.persistence.PersistenceException;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.exception.AbstractServerRtException;
|
||||
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement;
|
||||
import org.eclipse.hawkbit.repository.AutoAssignExecutor;
|
||||
import org.eclipse.hawkbit.repository.jpa.configuration.Constants;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
@@ -62,17 +64,15 @@ public class JpaAutoAssignExecutor implements AutoAssignExecutor {
|
||||
private final TargetManagement<? extends Target> targetManagement;
|
||||
private final DeploymentManagement deploymentManagement;
|
||||
private final PlatformTransactionManager transactionManager;
|
||||
private final ContextAware contextAware;
|
||||
|
||||
public JpaAutoAssignExecutor(
|
||||
final TargetFilterQueryManagement<? extends TargetFilterQuery> targetFilterQueryManagement,
|
||||
final TargetManagement<? extends Target> targetManagement, final DeploymentManagement deploymentManagement,
|
||||
final PlatformTransactionManager transactionManager, final ContextAware contextAware) {
|
||||
final PlatformTransactionManager transactionManager) {
|
||||
this.targetFilterQueryManagement = targetFilterQueryManagement;
|
||||
this.targetManagement = targetManagement;
|
||||
this.deploymentManagement = deploymentManagement;
|
||||
this.transactionManager = transactionManager;
|
||||
this.contextAware = contextAware;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,7 +84,7 @@ public class JpaAutoAssignExecutor implements AutoAssignExecutor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkSingleTarget(String controllerId) {
|
||||
public void checkSingleTarget(final String controllerId) {
|
||||
log.debug("Auto assign check call for device {} started", controllerId);
|
||||
forEachFilterWithAutoAssignDS(filter -> checkForDevice(controllerId, filter));
|
||||
log.debug("Auto assign check call for device {} finished", controllerId);
|
||||
@@ -127,7 +127,7 @@ public class JpaAutoAssignExecutor implements AutoAssignExecutor {
|
||||
|
||||
// run in the context the auto assignment is made in, i.e. if there is access control context it runs in it
|
||||
// otherwise in the tenant & user context built by createdBy
|
||||
// Note: It must be called in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant
|
||||
// Note: It must be called in a tenant context, i.e. Security.getCurrentTenant() returns the tenant
|
||||
private void forEachFilterWithAutoAssignDS(final Consumer<TargetFilterQuery> consumer) {
|
||||
Slice<TargetFilterQuery> filterQueries;
|
||||
Pageable query = PageRequest.of(0, PAGE_SIZE);
|
||||
@@ -137,14 +137,10 @@ public class JpaAutoAssignExecutor implements AutoAssignExecutor {
|
||||
filterQueries.forEach(filterQuery -> {
|
||||
try {
|
||||
filterQuery.getAccessControlContext().ifPresentOrElse(
|
||||
context -> // has stored context - executes it with it
|
||||
contextAware.runInContext(
|
||||
context,
|
||||
() -> consumer.accept(filterQuery)),
|
||||
() -> // has no stored context - executes it in the tenant & user scope
|
||||
contextAware.runAsTenantAsUser(
|
||||
contextAware.getCurrentTenant(),
|
||||
getAutoAssignmentInitiatedBy(filterQuery), () -> consumer.accept(filterQuery))
|
||||
// has stored context - executes it with it
|
||||
context -> withSecurityContext(context, () -> consumer.accept(filterQuery)),
|
||||
// has no stored context - executes it in the tenant & user scope
|
||||
() -> asActor(getAutoAssignmentInitiatedBy(filterQuery), () -> consumer.accept(filterQuery))
|
||||
);
|
||||
} catch (final RuntimeException ex) {
|
||||
if (log.isDebugEnabled()) {
|
||||
@@ -173,7 +169,9 @@ public class JpaAutoAssignExecutor implements AutoAssignExecutor {
|
||||
final List<DeploymentRequest> deploymentRequests = mapToDeploymentRequests(controllerIds, targetFilterQuery);
|
||||
final int count = deploymentRequests.size();
|
||||
if (count > 0) {
|
||||
deploymentManagement.assignDistributionSets(getAutoAssignmentInitiatedBy(targetFilterQuery), deploymentRequests, actionMessage);
|
||||
asActor(
|
||||
getAutoAssignmentInitiatedBy(targetFilterQuery),
|
||||
() -> deploymentManagement.assignDistributionSets(deploymentRequests, actionMessage));
|
||||
}
|
||||
return count;
|
||||
});
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.scheduler;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asActor;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.withSecurityContext;
|
||||
import static org.eclipse.hawkbit.repository.jpa.JpaManagementHelper.combineWithAnd;
|
||||
import static org.eclipse.hawkbit.repository.jpa.executor.AfterTransactionCommitExecutor.afterCommit;
|
||||
|
||||
@@ -25,7 +28,7 @@ import java.util.stream.StreamSupport;
|
||||
import jakarta.persistence.EntityManager;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.ContextAware;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.ql.jpa.QLSupport;
|
||||
import org.eclipse.hawkbit.repository.DeploymentManagement;
|
||||
import org.eclipse.hawkbit.repository.QuotaManagement;
|
||||
@@ -73,9 +76,6 @@ import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupStatus;
|
||||
import org.eclipse.hawkbit.repository.model.RolloutGroup.RolloutGroupSuccessCondition;
|
||||
import org.eclipse.hawkbit.repository.model.Target;
|
||||
import org.eclipse.hawkbit.repository.qfields.TargetFields;
|
||||
import org.eclipse.hawkbit.security.SpringSecurityAuditorAware;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.data.domain.Slice;
|
||||
@@ -127,9 +127,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
private final RolloutApprovalStrategy rolloutApprovalStrategy;
|
||||
private final EntityManager entityManager;
|
||||
private final PlatformTransactionManager txManager;
|
||||
private final TenantAware tenantAware;
|
||||
private final ContextAware contextAware;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final RepositoryProperties repositoryProperties;
|
||||
private final Map<Long, AtomicLong> lastDynamicGroupFill = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -142,7 +139,6 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
final RolloutManagement rolloutManagement, final QuotaManagement quotaManagement,
|
||||
final RolloutGroupEvaluationManager evaluationManager, final RolloutApprovalStrategy rolloutApprovalStrategy,
|
||||
final EntityManager entityManager, final PlatformTransactionManager txManager,
|
||||
final TenantAware tenantAware, final ContextAware contextAware, final SystemSecurityContext systemSecurityContext,
|
||||
final RepositoryProperties repositoryProperties) {
|
||||
this.actionRepository = actionRepository;
|
||||
this.rolloutGroupRepository = rolloutGroupRepository;
|
||||
@@ -157,19 +153,16 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
this.rolloutApprovalStrategy = rolloutApprovalStrategy;
|
||||
this.entityManager = entityManager;
|
||||
this.txManager = txManager;
|
||||
this.tenantAware = tenantAware;
|
||||
this.contextAware = contextAware;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.repositoryProperties = repositoryProperties;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final Rollout rollout) {
|
||||
rollout.getAccessControlContext().ifPresentOrElse(
|
||||
context -> // has stored context - executes it with it
|
||||
contextAware.runInContext(context, () -> execute0(rollout)),
|
||||
() -> // has no stored context - executes it in the tenant & user scope
|
||||
contextAware.runAsTenantAsUser(contextAware.getCurrentTenant(), rollout.getCreatedBy(), () -> execute0(rollout)));
|
||||
// has stored context - executes it with it
|
||||
context -> withSecurityContext(context, () -> execute0(rollout)),
|
||||
// has no stored context - executes it in the tenant & user scope
|
||||
() -> asActor(rollout.getCreatedBy(), () -> execute0(rollout)));
|
||||
}
|
||||
|
||||
private void execute0(final Rollout rollout) {
|
||||
@@ -184,36 +177,18 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
break;
|
||||
case STARTING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout start (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleStartingRollout((JpaRollout) rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
asActor(rollout.getLastModifiedBy(), () -> handleStartingRollout((JpaRollout) rollout));
|
||||
break;
|
||||
case RUNNING:
|
||||
handleRunningRollout((JpaRollout) rollout);
|
||||
break;
|
||||
case STOPPING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout stop (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleStopRollout((JpaRollout) rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
asActor(rollout.getLastModifiedBy(), () -> handleStopRollout((JpaRollout) rollout));
|
||||
break;
|
||||
case DELETING:
|
||||
// the lastModifiedBy user is probably the user that has actually called the rollout delete (unless overridden) - not the creator
|
||||
SpringSecurityAuditorAware.setAuditorOverride(rollout.getLastModifiedBy());
|
||||
try {
|
||||
handleDeleteRollout((JpaRollout) rollout);
|
||||
} finally {
|
||||
// clear, ALWAYS, the set auditor override
|
||||
SpringSecurityAuditorAware.clearAuditorOverride();
|
||||
}
|
||||
asActor(rollout.getLastModifiedBy(), () -> handleDeleteRollout((JpaRollout) rollout));
|
||||
break;
|
||||
default:
|
||||
log.error("Rollout in status {} not supposed to be handled!", rollout.getStatus());
|
||||
@@ -343,12 +318,12 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
|
||||
final List<Long> groupIds = rollout.getRolloutGroups().stream().map(RolloutGroup::getId).toList();
|
||||
afterCommit(() -> EventPublisherHolder.getInstance().getEventPublisher().publishEvent(
|
||||
new RolloutStoppedEvent(tenantAware.getCurrentTenant(), rollout.getId(), groupIds)));
|
||||
new RolloutStoppedEvent(AccessContext.tenant(), rollout.getId(), groupIds)));
|
||||
}
|
||||
}
|
||||
|
||||
private void handleReadyRollout(final Rollout rollout) {
|
||||
if (rollout.getStartAt() != null && rollout.getStartAt() <= System.currentTimeMillis()) {
|
||||
if (rollout.getStartAt() != null && rollout.getStartAt() <= java.lang.System.currentTimeMillis()) {
|
||||
log.debug("handleReadyRollout called for rollout {} with autostart beyond define time. Switch to STARTING", rollout.getId());
|
||||
rolloutManagement.start(rollout.getId());
|
||||
}
|
||||
@@ -671,7 +646,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
// return if group change is made
|
||||
private boolean fillDynamicRolloutGroupsWithTargets(final JpaRollout rollout) {
|
||||
final AtomicLong lastFill = lastDynamicGroupFill.computeIfAbsent(rollout.getId(), id -> new AtomicLong(0));
|
||||
final long now = System.currentTimeMillis();
|
||||
final long now = java.lang.System.currentTimeMillis();
|
||||
if (now - lastFill.get() < repositoryProperties.getDynamicRolloutsMinInvolvePeriodMS()) {
|
||||
// too early to make another dynamic involvement attempt
|
||||
return false;
|
||||
@@ -873,7 +848,7 @@ public class JpaRolloutExecutor implements RolloutExecutor {
|
||||
try {
|
||||
QuotaHelper.assertAssignmentQuota(target.getId(), requested, quota, Action.class, Target.class, actionRepository::countByTargetId);
|
||||
} catch (final AssignmentQuotaExceededException ex) {
|
||||
systemSecurityContext.runAsSystem(() -> deploymentManagement.handleMaxAssignmentsExceeded(target.getId(), requested, ex));
|
||||
asSystem(() -> deploymentManagement.handleMaxAssignmentsExceeded(target.getId(), requested, ex));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,12 @@ import java.util.concurrent.locks.Lock;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.RolloutExecutor;
|
||||
import org.eclipse.hawkbit.repository.RolloutHandler;
|
||||
import org.eclipse.hawkbit.repository.RolloutManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.utils.DeploymentHelper;
|
||||
import org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration;
|
||||
import org.eclipse.hawkbit.tenancy.TenantAware;
|
||||
import org.springframework.integration.support.locks.LockRegistry;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
|
||||
@@ -31,7 +31,6 @@ import org.springframework.transaction.PlatformTransactionManager;
|
||||
@Slf4j
|
||||
public class JpaRolloutHandler implements RolloutHandler {
|
||||
|
||||
private final TenantAware tenantAware;
|
||||
private final RolloutManagement rolloutManagement;
|
||||
private final RolloutExecutor rolloutExecutor;
|
||||
private final LockRegistry lockRegistry;
|
||||
@@ -41,16 +40,14 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param tenantAware the {@link TenantAware} bean holding the tenant information
|
||||
* @param rolloutManagement to fetch rollout related information from the datasource
|
||||
* @param rolloutExecutor to trigger executions for a specific rollout
|
||||
* @param lockRegistry to lock processes
|
||||
* @param txManager transaction manager interface
|
||||
*/
|
||||
public JpaRolloutHandler(final TenantAware tenantAware, final RolloutManagement rolloutManagement,
|
||||
public JpaRolloutHandler(final RolloutManagement rolloutManagement,
|
||||
final RolloutExecutor rolloutExecutor, final LockRegistry lockRegistry,
|
||||
final PlatformTransactionManager txManager, final Optional<MeterRegistry> meterRegistry) {
|
||||
this.tenantAware = tenantAware;
|
||||
this.rolloutManagement = rolloutManagement;
|
||||
this.rolloutExecutor = rolloutExecutor;
|
||||
this.lockRegistry = lockRegistry;
|
||||
@@ -65,7 +62,7 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
final String handlerId = createRolloutLockKey(tenantAware.getCurrentTenant());
|
||||
final String handlerId = createRolloutLockKey(AccessContext.tenant());
|
||||
final Lock lock = lockRegistry.obtain(handlerId);
|
||||
if (!lock.tryLock()) {
|
||||
if (log.isTraceEnabled()) {
|
||||
@@ -86,7 +83,7 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
}
|
||||
});
|
||||
meterRegistry
|
||||
.map(mReg -> mReg.timer("hawkbit.rollout.handler.all", DefaultTenantConfiguration.TENANT_TAG, tenantAware.getCurrentTenant()))
|
||||
.map(mReg -> mReg.timer("hawkbit.rollout.handler.all", DefaultTenantConfiguration.TENANT_TAG, AccessContext.tenant()))
|
||||
.ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
|
||||
log.debug("Finished handling of the rollouts.");
|
||||
@@ -102,7 +99,7 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
return tenant + "-rollout";
|
||||
}
|
||||
|
||||
// run in a tenant context, i.e. contextAware.getCurrentTenant() returns the tenant the rollout is made for
|
||||
// run in a tenant context, i.e. Security.getCurrentTenant() returns the tenant the rollout is made for
|
||||
private void handleRolloutInNewTransaction(final long rolloutId, final String handlerId) {
|
||||
final long startNano = System.nanoTime();
|
||||
|
||||
@@ -116,7 +113,7 @@ public class JpaRolloutHandler implements RolloutHandler {
|
||||
meterRegistry
|
||||
.map(mReg -> mReg.timer(
|
||||
"hawkbit.rollout.handler",
|
||||
DefaultTenantConfiguration.TENANT_TAG, tenantAware.getCurrentTenant(),
|
||||
DefaultTenantConfiguration.TENANT_TAG, AccessContext.tenant(),
|
||||
"rollout", String.valueOf(rolloutId)))
|
||||
.ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.scheduler;
|
||||
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystem;
|
||||
import static org.eclipse.hawkbit.context.AccessContext.asSystemAsTenant;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -17,7 +20,6 @@ import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.hawkbit.repository.RolloutHandler;
|
||||
import org.eclipse.hawkbit.repository.SystemManagement;
|
||||
import org.eclipse.hawkbit.repository.jpa.rollout.BlockWhenFullPolicy;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.DefaultTenantConfiguration;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
@@ -34,75 +36,63 @@ public class RolloutScheduler {
|
||||
|
||||
private final SystemManagement systemManagement;
|
||||
private final RolloutHandler rolloutHandler;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
private final Optional<MeterRegistry> meterRegistry;
|
||||
private final ThreadPoolTaskExecutor rolloutTaskExecutor;
|
||||
|
||||
public RolloutScheduler(
|
||||
final RolloutHandler rolloutHandler, final SystemManagement systemManagement, final SystemSecurityContext systemSecurityContext,
|
||||
final RolloutHandler rolloutHandler, final SystemManagement systemManagement,
|
||||
final int threadPoolSize, final Optional<MeterRegistry> meterRegistry) {
|
||||
this.systemManagement = systemManagement;
|
||||
this.rolloutHandler = rolloutHandler;
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.meterRegistry = meterRegistry;
|
||||
rolloutTaskExecutor = threadPoolTaskExecutor(threadPoolSize);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Scheduler method called by the spring-async mechanism. Retrieves all tenants from the {@link SystemManagement#findTenants} and
|
||||
* runs for each tenant the {@link RolloutHandler#handleAll()} in the {@link SystemSecurityContext}.
|
||||
* Scheduler method called by the spring-async mechanism. For all tenants, using {@link SystemManagement#forEachTenant},
|
||||
* runs the {@link RolloutHandler#handleAll()} scoped to permission of access control context or unscoped (with {@link System}) if null
|
||||
*/
|
||||
@Scheduled(initialDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER, fixedDelayString = PROP_SCHEDULER_DELAY_PLACEHOLDER)
|
||||
public void runningRolloutScheduler() {
|
||||
log.debug("rollout schedule checker has been triggered.");
|
||||
final long startNano = System.nanoTime();
|
||||
final long startNano = java.lang.System.nanoTime();
|
||||
|
||||
// run this code in system code privileged to have the necessary
|
||||
// permission to query and create entities.
|
||||
systemSecurityContext.runAsSystem(() -> {
|
||||
// workaround eclipselink that is currently not possible to
|
||||
// execute a query without multi-tenancy if MultiTenant
|
||||
// annotation is used.
|
||||
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
|
||||
// iterate through all tenants and execute the rollout check for
|
||||
// each tenant separately.
|
||||
systemManagement.forEachTenant(tenant -> {
|
||||
if (rolloutTaskExecutor == null) {
|
||||
handleAll(tenant);
|
||||
} else {
|
||||
handleAllAsync(tenant);
|
||||
}
|
||||
});
|
||||
});
|
||||
// run this code in system code privileged to have the necessary system code permission execute forEachTenant
|
||||
asSystem(() ->
|
||||
// workaround eclipselink that is currently not possible to execute a query without multi-tenancy if MultiTenant
|
||||
// annotation is used. https://bugs.eclipse.org/bugs/show_bug.cgi?id=355458. So
|
||||
// iterate through all tenants and execute the rollout check for each tenant separately.
|
||||
systemManagement.forEachTenant(tenant -> {
|
||||
if (rolloutTaskExecutor == null) {
|
||||
handleAll(tenant);
|
||||
} else {
|
||||
handleAllAsync(tenant);
|
||||
}
|
||||
}));
|
||||
|
||||
meterRegistry
|
||||
.map(mReg -> mReg.timer("hawkbit.rollout.scheduler.all"))
|
||||
.ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
.ifPresent(timer -> timer.record(java.lang.System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
}
|
||||
|
||||
private void handleAll(final String tenant) {
|
||||
log.trace("Handling rollout for tenant: {}", tenant);
|
||||
final long startNano = System.nanoTime();
|
||||
final long startNano = java.lang.System.nanoTime();
|
||||
|
||||
try {
|
||||
rolloutHandler.handleAll();
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
log.error("Error processing rollout for tenant {}", tenant, e);
|
||||
}
|
||||
|
||||
meterRegistry
|
||||
.map(mReg -> mReg.timer(
|
||||
"hawkbit.rollout.scheduler",
|
||||
DefaultTenantConfiguration.TENANT_TAG, tenant))
|
||||
.ifPresent(timer -> timer.record(System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
.map(mReg -> mReg.timer("hawkbit.rollout.scheduler", DefaultTenantConfiguration.TENANT_TAG, tenant))
|
||||
.ifPresent(timer -> timer.record(java.lang.System.nanoTime() - startNano, TimeUnit.NANOSECONDS));
|
||||
}
|
||||
|
||||
private void handleAllAsync(final String tenant) {
|
||||
rolloutTaskExecutor.submit(() -> systemSecurityContext.runAsSystemAsTenant(() -> {
|
||||
handleAll(tenant);
|
||||
return null;
|
||||
}, tenant));
|
||||
rolloutTaskExecutor.submit(() -> asSystemAsTenant(tenant, () -> handleAll(tenant)));
|
||||
}
|
||||
|
||||
private ThreadPoolTaskExecutor threadPoolTaskExecutor(final int threadPoolSize) {
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.criteria.Join;
|
||||
import jakarta.persistence.criteria.ListJoin;
|
||||
import jakarta.persistence.criteria.SetJoin;
|
||||
@@ -29,8 +31,6 @@ import org.eclipse.hawkbit.repository.jpa.model.JpaTarget_;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Utility class for {@link Action}s {@link Specification}s. The class provides Spring Data JPQL Specifications.
|
||||
*/
|
||||
|
||||
@@ -11,10 +11,8 @@ package org.eclipse.hawkbit.repository.jpa.specifications;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaNamedEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.AbstractJpaTypeEntity_;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaDistributionSetType_;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSet;
|
||||
import org.eclipse.hawkbit.repository.model.DistributionSetType;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
|
||||
@@ -11,46 +11,25 @@ package org.eclipse.hawkbit.repository.jpa.utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.NoArgsConstructor;
|
||||
import org.eclipse.hawkbit.repository.TargetFilterQueryManagement;
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.exception.NoWeightProvidedInMultiAssignmentModeException;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.model.DeploymentRequest;
|
||||
import org.eclipse.hawkbit.repository.model.Rollout;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.utils.TenantConfigHelper;
|
||||
|
||||
/**
|
||||
* Utility class to handle weight validation in Rollout, Auto Assignments, and
|
||||
* Online Assignment.
|
||||
* Utility class to handle weight validation in Rollout, Auto Assignments, and Online Assignment.
|
||||
*/
|
||||
@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
|
||||
public final class WeightValidationHelper {
|
||||
|
||||
private final TenantConfigurationManagement tenantConfigurationManagement;
|
||||
private final SystemSecurityContext systemSecurityContext;
|
||||
|
||||
private WeightValidationHelper(
|
||||
final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
this.systemSecurityContext = systemSecurityContext;
|
||||
this.tenantConfigurationManagement = tenantConfigurationManagement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting the context of the tenant
|
||||
*
|
||||
* @param systemSecurityContext security context used to get the tenant and for execution
|
||||
* @param tenantConfigurationManagement to get the value from
|
||||
*/
|
||||
public static WeightValidationHelper usingContext(
|
||||
final SystemSecurityContext systemSecurityContext, final TenantConfigurationManagement tenantConfigurationManagement) {
|
||||
return new WeightValidationHelper(systemSecurityContext, tenantConfigurationManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validating weights associated with all the {@link DeploymentRequest}s
|
||||
*
|
||||
* @param deploymentRequests the {@linkplain List} of {@link DeploymentRequest}s
|
||||
*/
|
||||
public void validate(final List<DeploymentRequest> deploymentRequests) {
|
||||
public static void validate(final List<DeploymentRequest> deploymentRequests) {
|
||||
final long assignmentsWithWeight = deploymentRequests.stream()
|
||||
.filter(request -> request.getTargetWithActionType().getWeight() != null).count();
|
||||
final boolean containsAssignmentWithWeight = assignmentsWithWeight > 0;
|
||||
@@ -64,7 +43,7 @@ public final class WeightValidationHelper {
|
||||
*
|
||||
* @param rollout the {@linkplain Rollout}
|
||||
*/
|
||||
public void validate(final Rollout rollout) {
|
||||
public static void validate(final Rollout rollout) {
|
||||
validateWeight(rollout.getWeight().orElse(null));
|
||||
}
|
||||
|
||||
@@ -73,7 +52,7 @@ public final class WeightValidationHelper {
|
||||
*
|
||||
* @param targetFilterQueryCreate the target filter query
|
||||
*/
|
||||
public void validate(final TargetFilterQueryManagement.Create targetFilterQueryCreate) {
|
||||
public static void validate(final TargetFilterQueryManagement.Create targetFilterQueryCreate) {
|
||||
validateWeight(targetFilterQueryCreate.getAutoAssignWeight());
|
||||
}
|
||||
|
||||
@@ -82,7 +61,7 @@ public final class WeightValidationHelper {
|
||||
*
|
||||
* @param autoAssignDistributionSetUpdate the auto assignment distribution set update
|
||||
*/
|
||||
public void validate(final TargetFilterQueryManagement.AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate) {
|
||||
public static void validate(final TargetFilterQueryManagement.AutoAssignDistributionSetUpdate autoAssignDistributionSetUpdate) {
|
||||
validateWeight(autoAssignDistributionSetUpdate.weight());
|
||||
}
|
||||
|
||||
@@ -91,7 +70,7 @@ public final class WeightValidationHelper {
|
||||
*
|
||||
* @param weight weight tied to the rollout, auto assignment, or online assignment.
|
||||
*/
|
||||
public void validateWeight(final Integer weight) {
|
||||
public static void validateWeight(final Integer weight) {
|
||||
final boolean hasWeight = weight != null;
|
||||
validateWeight(hasWeight, !hasWeight);
|
||||
}
|
||||
@@ -102,15 +81,11 @@ public final class WeightValidationHelper {
|
||||
* @param hasWeight indicator of the weight if it has numerical value
|
||||
* @param hasNoWeight indicator of the weight if it doesn't have a numerical value
|
||||
*/
|
||||
public void validateWeight(final boolean hasWeight, final boolean hasNoWeight) {
|
||||
public static void validateWeight(final boolean hasWeight, final boolean hasNoWeight) {
|
||||
// remove bypassing the weight enforcement as soon as weight can be set via UI
|
||||
final boolean bypassWeightEnforcement = true;
|
||||
final boolean multiAssignmentsEnabled = TenantConfigHelper
|
||||
.usingContext(systemSecurityContext, tenantConfigurationManagement)
|
||||
.isMultiAssignmentsEnabled();
|
||||
if (bypassWeightEnforcement) {
|
||||
return;
|
||||
} else if (multiAssignmentsEnabled && hasNoWeight) {
|
||||
final boolean multiAssignmentsEnabled = TenantConfigHelper.isMultiAssignmentsEnabled();
|
||||
if (!bypassWeightEnforcement && multiAssignmentsEnabled && hasNoWeight) {
|
||||
throw new NoWeightProvidedInMultiAssignmentModeException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ package org.eclipse.hawkbit.ql.rsql;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.TargetManagement.Create;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterSyntaxException;
|
||||
import org.eclipse.hawkbit.repository.exception.RSQLParameterUnsupportedFieldException;
|
||||
@@ -117,7 +118,7 @@ class RsqlActionFieldsTest extends AbstractJpaIntegrationTest {
|
||||
newAction.setStatus(active ? Status.RUNNING : Status.FINISHED);
|
||||
newAction.setTarget(target);
|
||||
newAction.setWeight(45);
|
||||
newAction.setInitiatedBy(tenantAware.getCurrentUsername());
|
||||
newAction.setInitiatedBy(AccessContext.actor());
|
||||
if (extRef != null) {
|
||||
newAction.setExternalRef(extRef);
|
||||
}
|
||||
|
||||
@@ -12,23 +12,16 @@ package org.eclipse.hawkbit.ql.rsql;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import org.eclipse.hawkbit.repository.TenantConfigurationManagement;
|
||||
import org.eclipse.hawkbit.repository.helper.SystemSecurityContextHolder;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigurationManagementHolder;
|
||||
import org.eclipse.hawkbit.repository.helper.TenantConfigHelper;
|
||||
import org.eclipse.hawkbit.repository.model.TenantConfigurationValue;
|
||||
import org.eclipse.hawkbit.repository.rsql.VirtualPropertyResolver;
|
||||
import org.eclipse.hawkbit.security.SystemSecurityContext;
|
||||
import org.eclipse.hawkbit.tenancy.configuration.TenantConfigurationProperties.TenantConfigurationKey;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||
import org.springframework.test.context.junit.jupiter.SpringExtension;
|
||||
|
||||
@@ -45,18 +38,17 @@ class VirtualPropertyResolverTest {
|
||||
TenantConfigurationValue.<String> builder().value("00:07:37").build();
|
||||
|
||||
@MockitoBean
|
||||
private TenantConfigurationManagement confMgmt;
|
||||
@MockitoBean
|
||||
private SystemSecurityContext securityContext;
|
||||
private TenantConfigurationManagement tenantConfigurationManagement;
|
||||
|
||||
private final VirtualPropertyResolver substitutor = new VirtualPropertyResolver();
|
||||
|
||||
@BeforeEach
|
||||
void before() {
|
||||
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class))
|
||||
when(tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.POLLING_TIME, String.class))
|
||||
.thenReturn(TEST_POLLING_TIME_INTERVAL);
|
||||
when(confMgmt.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class))
|
||||
when(tenantConfigurationManagement.getConfigurationValue(TenantConfigurationKey.POLLING_OVERDUE_TIME, String.class))
|
||||
.thenReturn(TEST_POLLING_OVERDUE_TIME_INTERVAL);
|
||||
TenantConfigHelper.setTenantConfigurationManagement(tenantConfigurationManagement);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -90,24 +82,9 @@ class VirtualPropertyResolverTest {
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = { "${NOW_TS}", "${OVERDUE_TS}", "${overdue_ts}" })
|
||||
void resolveNowTimestampPlaceholder(final String placeholder) {
|
||||
when(securityContext.runAsSystem(Mockito.any(Callable.class))).thenAnswer(a -> ((Callable<?>) a.getArgument(0)).call());
|
||||
final String testString = "lhs=lt=" + placeholder;
|
||||
|
||||
final String resolvedPlaceholders = substitutor.replace(testString);
|
||||
assertThat(resolvedPlaceholders).as("'%s' placeholder was not replaced", placeholder).doesNotContain(placeholder);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class Config {
|
||||
|
||||
@Bean
|
||||
TenantConfigurationManagementHolder tenantConfigurationManagementHolder() {
|
||||
return TenantConfigurationManagementHolder.getInstance();
|
||||
}
|
||||
|
||||
@Bean
|
||||
SystemSecurityContextHolder systemSecurityContextHolder() {
|
||||
return SystemSecurityContextHolder.getInstance();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,9 @@
|
||||
* SPDX-License-Identifier: EPL-2.0
|
||||
*/
|
||||
package org.eclipse.hawkbit.repository.event.remote;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.hawkbit.event.EventJacksonMessageConverter;
|
||||
import org.eclipse.hawkbit.event.EventProtoStuffMessageConverter;
|
||||
import org.eclipse.hawkbit.repository.event.TenantAwareEvent;
|
||||
|
||||
@@ -13,6 +13,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.hawkbit.context.AccessContext;
|
||||
import org.eclipse.hawkbit.repository.jpa.model.JpaAction;
|
||||
import org.eclipse.hawkbit.repository.model.Action;
|
||||
import org.eclipse.hawkbit.repository.model.Action.ActionType;
|
||||
@@ -25,7 +26,7 @@ import org.junit.jupiter.api.Test;
|
||||
* Feature: Component Tests - Repository<br/>
|
||||
* Story: RemoteTenantAwareEvent Tests
|
||||
*/
|
||||
class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
class RemoteTenantEventTest extends AbstractRemoteEventTest {
|
||||
|
||||
private static final String TENANT_DEFAULT = "DEFAULT";
|
||||
|
||||
@@ -93,7 +94,7 @@ class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
generateAction.setTarget(testdataFactory.createTarget("Test"));
|
||||
generateAction.setDistributionSet(dsA);
|
||||
generateAction.setStatus(Status.RUNNING);
|
||||
generateAction.setInitiatedBy(tenantAware.getCurrentUsername());
|
||||
generateAction.setInitiatedBy(AccessContext.actor());
|
||||
generateAction.setWeight(1000);
|
||||
|
||||
final Action action = actionRepository.save(generateAction);
|
||||
@@ -120,7 +121,7 @@ class RemoteTenantAwareEventTest extends AbstractRemoteEventTest {
|
||||
generateAction.setTarget(testdataFactory.createTarget("Test"));
|
||||
generateAction.setDistributionSet(dsA);
|
||||
generateAction.setStatus(Status.RUNNING);
|
||||
generateAction.setInitiatedBy(tenantAware.getCurrentUsername());
|
||||
generateAction.setInitiatedBy(AccessContext.actor());
|
||||
generateAction.setWeight(1000);
|
||||
|
||||
final Action action = actionRepository.save(generateAction);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user