Skip to content

feat: container temporary credentials#5965

Merged
MMaiero merged 52 commits intodevelopfrom
feature/container-temporary-credentials
Feb 24, 2026
Merged

feat: container temporary credentials#5965
MMaiero merged 52 commits intodevelopfrom
feature/container-temporary-credentials

Conversation

@MMaiero
Copy link
Copy Markdown
Member

@MMaiero MMaiero commented Sep 8, 2025

Note: We are using the Conventional Commits convention for our pull request titles. Please take a look at the PR title format document for the supported types and scopes.

Brief description of the PR. [e.g. Added null check on object to avoid NullPointerException]

Related Issue: This PR fixes/closes {issue number}

Description of the solution adopted: A more detailed description of the changes made to solve/close one or more issues. If the PR is simple and easy to understand this section can be skipped

Screenshots: If applicable, add screenshots to help explain your solution

Manual Tests: Optional description of the tests performed to check correct functioning of changes, useful for an efficient review

Any side note on the changes made: Description of any other change that has been made, which is not directly linked to the issue resolution [e.g. Code clean up/Sonar issue resolution]

@MMaiero MMaiero requested a review from Copilot September 8, 2025 07:22
@MMaiero MMaiero changed the title Feature/container temporary credentials feat: container temporary credentials Sep 8, 2025
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces a temporary identity service feature for container authentication that allows containers to be provisioned with temporary credentials for accessing Kura REST APIs, enhancing security by eliminating the need for persistent credentials.

  • Implements TemporaryIdentityService API and core functionality for managing in-memory temporary identities
  • Adds container identity integration to automatically provision temporary credentials for containers
  • Introduces REST authentication provider for validating temporary tokens

Reviewed Changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
kura/org.eclipse.kura.api/src/main/java/org/eclipse/kura/identity/TemporaryIdentityService.java Defines the new API interface for temporary identity management
kura/org.eclipse.kura.core.identity/src/main/java/org/eclipse/kura/core/identity/IdentityServiceImpl.java Implements temporary identity service functionality within the existing identity service
kura/org.eclipse.kura.core.identity/src/main/java/org/eclipse/kura/core/identity/TemporaryIdentity.java Data model for temporary identity objects with permissions and tokens
kura/org.eclipse.kura.rest.provider/src/main/java/org/eclipse/kura/internal/rest/auth/TemporaryTokenAuthenticationProvider.java REST authentication provider for validating temporary tokens
kura/org.eclipse.kura.rest.provider/src/main/java/org/eclipse/kura/internal/rest/provider/AuthenticationFilter.java Updates authentication filter to support temporary token validation
kura/org.eclipse.kura.container.provider/src/main/java/org/eclipse/kura/container/provider/ContainerInstance.java Container integration to create and inject temporary credentials
kura/org.eclipse.kura.container.provider/src/main/java/org/eclipse/kura/container/provider/ContainerInstanceOptions.java Configuration options for container identity integration
kura/test/org.eclipse.kura.core.identity.test/src/main/java/org/eclipse/kura/core/identity/test/TemporaryIdentityServiceTest.java Comprehensive test suite for temporary identity functionality

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +134 to +137
// Check if this is a temporary token principal by examining the class name
if (requestUser.getClass().getSimpleName().contains("TemporaryToken")) {
return isTemporaryUserInRole(requestUser, role);
}
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using class name comparison is fragile and error-prone. Consider using instanceof check or implementing a marker interface/method to identify temporary token principals instead of string comparison on class name.

Copilot uses AI. Check for mistakes.
Comment on lines +156 to +157
// Get the token using reflection
final String token = (String) principal.getClass().getMethod("getToken").invoke(principal);
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using reflection to access the token is fragile and can fail at runtime. Consider defining an interface with a getToken() method that the TemporaryTokenPrincipal can implement, allowing for type-safe access.

Copilot uses AI. Check for mistakes.
.setDeviceList(baseConfig.getContainerDevices())
.setFrameworkManaged(baseConfig.isFrameworkManaged())
.setLoggingType(baseConfig.getContainerLoggingType())
.setContainerNetowrkConfiguration(baseConfig.getContainerNetworkConfiguration())
Copy link

Copilot AI Sep 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a typo in the method name. It should be "setContainerNetworkConfiguration" instead of "setContainerNetowrkConfiguration".

Suggested change
.setContainerNetowrkConfiguration(baseConfig.getContainerNetworkConfiguration())
.setContainerNetworkConfiguration(baseConfig.getContainerNetworkConfiguration())

Copilot uses AI. Check for mistakes.
@MMaiero MMaiero requested a review from Copilot December 9, 2025 16:58
@MMaiero MMaiero force-pushed the feature/container-temporary-credentials branch from ff7fd30 to ac65a92 Compare December 9, 2025 17:04
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 22 changed files in this pull request and generated 22 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +500 to +521
private void createTemporaryIdentityIfEnabled(final ContainerInstanceOptions options) {
if (options.isIdentityIntegrationEnabled() && this.temporaryIdentityService != null) {
try {
cleanupTemporaryIdentity();

final Set<Permission> permissions = options.getContainerPermissions().stream()
.map(Permission::new)
.collect(Collectors.toSet());

final String identityName = "container_" + options.getContainerName().replace("-", "_");
this.currentTemporaryToken.set(
this.temporaryIdentityService.createTemporaryIdentity(identityName, permissions));

logger.info("Created temporary identity for container {} with {} permissions",
options.getContainerName(), permissions.size());

} catch (KuraException e) {
logger.error("Failed to create temporary identity for container {}", options.getContainerName(), e);
this.currentTemporaryToken.set(null);
}
}
}
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Javadoc documentation for the createTemporaryIdentityIfEnabled() method. Private helper methods that encapsulate complex logic should include documentation explaining their purpose, especially when they handle important features like identity management.

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +169
private boolean isTemporaryUserInRole(final TokenHolder tokenHolder, final String role) {
if (this.temporaryIdentityService == null) {
return false;
}

try {
final String token = tokenHolder.getToken();

if (token == null) {
return false;
}

final Permission permission = new Permission("rest." + role);
this.temporaryIdentityService.checkTemporaryPermission(token, permission);
return true;
} catch (final Exception e) {
return false;
}
}
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Javadoc documentation for the isTemporaryUserInRole() method. Private helper methods that implement authorization logic should include documentation explaining their purpose, parameters, and return values.

Copilot uses AI. Check for mistakes.
Comment on lines +31 to +35
public TemporaryIdentity(String name, Set<Permission> permissions, String token) {
this.name = Objects.requireNonNull(name, "name cannot be null");
this.permissions = Objects.requireNonNull(permissions, "permissions cannot be null");
this.token = Objects.requireNonNull(token, "token cannot be null");
this.creationTime = System.currentTimeMillis();
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name and token fields are used in field assignments but are not prefixed with this.. Based on the codebase convention, these should be this.name = Objects.requireNonNull(name, "name cannot be null"); and similar for other fields for consistency.

Copilot uses AI. Check for mistakes.
this.token = Objects.requireNonNull(token, "token cannot be null");
this.creationTime = System.currentTimeMillis();
}

Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Javadoc documentation for the getName() getter method. Public API methods in this codebase should have Javadoc comments describing their purpose and return values.

Suggested change
/**
* Returns the name associated with this temporary identity.
*
* @return the identity name
*/

Copilot uses AI. Check for mistakes.
<AD id="container.identity.enabled" name="Enable Identity Integration" type="Boolean" cardinality="1" required="true" default="false"
description="Enable integration with Kura Identity Service to provide temporary credentials to the container. Default: false">
</AD>

Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace on this line. This should be removed to maintain code cleanliness and consistency with the project's formatting standards.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +394 to +395
createTemporaryIdentityIfEnabled(options);
final ContainerConfiguration containerConfiguration = getContainerConfigurationWithCredentials(options);
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential race condition: The createTemporaryIdentityIfEnabled() method calls cleanupTemporaryIdentity() at the start, and getContainerConfigurationWithCredentials() reads this.currentTemporaryToken.get() immediately after. If these are called from different threads or if there's any interruption between calls, the token might be null when building the configuration. Consider ensuring thread safety or documenting the expected calling context.

Copilot uses AI. Check for mistakes.
Comment on lines +104 to +106
public TemporaryTokenPrincipal(String name, String token) {
this.name = name;
this.token = token;
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name and token fields are used in field assignments but are not prefixed with this.. Based on the codebase convention, these should be this.name = name; and this.token = token; for consistency with other similar code patterns in the repository.

Copilot uses AI. Check for mistakes.
Comment on lines +41 to +53

public Set<Permission> getPermissions() {
return permissions;
}

public long getCreationTime() {
return creationTime;
}

public String getToken() {
return token;
}

Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing Javadoc documentation for the getPermissions(), getCreationTime(), getToken(), and hasPermission() methods. Public methods in this codebase should have Javadoc comments describing their purpose, parameters, and return values.

Suggested change
public Set<Permission> getPermissions() {
return permissions;
}
public long getCreationTime() {
return creationTime;
}
public String getToken() {
return token;
}
/**
* Returns the set of permissions associated with this identity.
*
* @return the set of permissions
*/
public Set<Permission> getPermissions() {
return permissions;
}
/**
* Returns the creation time of this identity in milliseconds since the epoch.
*
* @return the creation time in milliseconds
*/
public long getCreationTime() {
return creationTime;
}
/**
* Returns the token associated with this identity.
*
* @return the identity token
*/
public String getToken() {
return token;
}
/**
* Checks if this identity has the specified permission.
*
* @param permission the permission to check
* @return {@code true} if the identity has the permission, {@code false} otherwise
*/

Copilot uses AI. Check for mistakes.

private ContainerConfiguration getContainerConfigurationWithCredentials(final ContainerInstanceOptions options) {
ContainerConfiguration baseConfig = options.getContainerConfiguration();

Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trailing whitespace on this line. This should be removed to maintain code cleanliness and consistency with the project's formatting standards.

Copilot uses AI. Check for mistakes.
@MMaiero MMaiero force-pushed the feature/container-temporary-credentials branch from 916bfb5 to 2ddf154 Compare December 17, 2025 16:59
@MMaiero MMaiero marked this pull request as ready for review December 24, 2025 15:08
@MMaiero MMaiero force-pushed the feature/container-temporary-credentials branch from 9c4e805 to 928bb18 Compare December 24, 2025 15:08
@MMaiero MMaiero requested a review from nicolatimeus December 24, 2025 15:08
@MMaiero
Copy link
Copy Markdown
Member Author

MMaiero commented Jan 7, 2026

To verify and validate, via Kura create a new container with the feature for passing credentials enabled and the right permissions set. For example the rest.inventory. Then try to interact with Kura REST via the container just started for example using:

sudo docker exec -it 828037e1b6c3 sh -lc 'curl -k -sS -i -H "Authorization: Kura-Token $KURA_IDENTITY_TOKEN" "https://172.17.0.1:443/services/inventory/v1/inventory"'

* @return a temporary authentication token that can be used to authenticate as this identity.
* @throws KuraException if a failure occurs in creating the temporary identity.
*/
public String createTemporaryIdentity(final String identityName, final Set<Permission> permissions) throws KuraException;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove 'Temporary' from the names of all methods. 'Temporary' is implied by the service name.

@nicolatimeus
Copy link
Copy Markdown
Member

This PR introduces a new concept of temporary identity and a new token based authentication mechanism, in my opinion the two topics are orthogonal.

In my opinion the only difference between regular and temporary identities should be that the latter are not persisted in the snapshot and have a fixed lifetime period, but I would expect them to behave like the regular ones for all other aspects (for example being able to use the existing auth methods for them). We could for example create a separate in-memory store for them that contains the same informations that we store in the UserAdmin that is used by the IdentityService in addition to the UserAdmin.

A blocker for implementing this approach is the fact that the REST service is currently using the UserAdmin directly for authentication, and has not ported to use the new IdentityService yet.

We could also implement a new token based authentication method that works the same both for regular and temporary identities, here we could use a standard solution like JWT.

I would proceed with the following steps, maybe with separate contributions that can be probably done in parallel:

  • Remove the dependency from the REST service to the UserAdmin and use the new IdentityService instead
  • Introduce the concept of temporary identities that behave as similar as possible to regular ones
  • Add support for a token-based authentication service that works both for regular and temporary identities

@MMaiero
Copy link
Copy Markdown
Member Author

MMaiero commented Jan 7, 2026

This PR introduces a new concept of temporary identity and a new token based authentication mechanism, in my opinion the two topics are orthogonal.

In my opinion the only difference between regular and temporary identities should be that the latter are not persisted in the snapshot and have a fixed lifetime period, but I would expect them to behave like the regular ones for all other aspects (for example being able to use the existing auth methods for them). We could for example create a separate in-memory store for them that contains the same informations that we store in the UserAdmin that is used by the IdentityService in addition to the UserAdmin.

A blocker for implementing this approach is the fact that the REST service is currently using the UserAdmin directly for authentication, and has not ported to use the new IdentityService yet.

We could also implement a new token based authentication method that works the same both for regular and temporary identities, here we could use a standard solution like JWT.

I would proceed with the following steps, maybe with separate contributions that can be probably done in parallel:

  • Remove the dependency from the REST service to the UserAdmin and use the new IdentityService instead
  • Introduce the concept of temporary identities that behave as similar as possible to regular ones
  • Add support for a token-based authentication service that works both for regular and temporary identities

But would make sense then to use password based auth or certificate auth from the containers?

@MMaiero MMaiero force-pushed the feature/container-temporary-credentials branch from 1844d56 to 19e6427 Compare January 7, 2026 16:09
@MMaiero MMaiero requested a review from Copilot January 10, 2026 15:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 51 out of 51 changed files in this pull request and generated 13 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +21 to +79
* Represents a temporary identity that exists only in memory.
* Used for container authentication without persistent storage.
*/
public class TemporaryIdentity {

private final String name;
private final Set<Permission> permissions;
private final long creationTime;
private final String token;

public TemporaryIdentity(String name, Set<Permission> permissions, String token) {
this.name = Objects.requireNonNull(name, "name cannot be null");
this.permissions = Objects.requireNonNull(permissions, "permissions cannot be null");
this.token = Objects.requireNonNull(token, "token cannot be null");
this.creationTime = System.currentTimeMillis();
}

public String getName() {
return name;
}

public Set<Permission> getPermissions() {
return permissions;
}

public long getCreationTime() {
return creationTime;
}

public String getToken() {
return token;
}

public boolean hasPermission(Permission permission) {
return permissions.contains(permission);
}

@Override
public int hashCode() {
return Objects.hash(name, token);
}

@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof TemporaryIdentity)) {
return false;
}
TemporaryIdentity other = (TemporaryIdentity) obj;
return Objects.equals(name, other.name) && Objects.equals(token, other.token);
}

@Override
public String toString() {
return "TemporaryIdentity{" + "name='" + name + '\'' + ", permissions=" + permissions.size() + ", creationTime="
+ creationTime + '}';
}
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TemporaryIdentity class is defined but never used in the implementation. The temporary identity functionality uses IdentityConfiguration directly instead. This class should either be removed or integrated into the design if it serves a purpose.

Suggested change
* Represents a temporary identity that exists only in memory.
* Used for container authentication without persistent storage.
*/
public class TemporaryIdentity {
private final String name;
private final Set<Permission> permissions;
private final long creationTime;
private final String token;
public TemporaryIdentity(String name, Set<Permission> permissions, String token) {
this.name = Objects.requireNonNull(name, "name cannot be null");
this.permissions = Objects.requireNonNull(permissions, "permissions cannot be null");
this.token = Objects.requireNonNull(token, "token cannot be null");
this.creationTime = System.currentTimeMillis();
}
public String getName() {
return name;
}
public Set<Permission> getPermissions() {
return permissions;
}
public long getCreationTime() {
return creationTime;
}
public String getToken() {
return token;
}
public boolean hasPermission(Permission permission) {
return permissions.contains(permission);
}
@Override
public int hashCode() {
return Objects.hash(name, token);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof TemporaryIdentity)) {
return false;
}
TemporaryIdentity other = (TemporaryIdentity) obj;
return Objects.equals(name, other.name) && Objects.equals(token, other.token);
}
@Override
public String toString() {
return "TemporaryIdentity{" + "name='" + name + '\'' + ", permissions=" + permissions.size() + ", creationTime="
+ creationTime + '}';
}
* @deprecated This class is no longer used. Temporary identity functionality
* relies on {@code IdentityConfiguration} directly. This type is
* retained only for backwards compatibility and should not be used
* in new code.
*/
@Deprecated
public final class TemporaryIdentity {
private TemporaryIdentity() {
throw new AssertionError("TemporaryIdentity is deprecated and should not be used");
}

Copilot uses AI. Check for mistakes.
Comment on lines +429 to +433
// Create temporary identity with very long lifetime (365 days)
// The identity lifetime matches the container lifecycle - cleanup happens when container stops
// The duration is a safety net for cases where cleanup fails
ContainerInstance.this.identityService.createTemporaryIdentity(identityName, configuration,
Duration.ofDays(365));
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The duration of 365 days for temporary identities is extremely long for something called "temporary". This undermines the security benefit of temporary credentials. Consider using a much shorter default lifetime (e.g., hours or days) or making this configurable. The comment mentions it's a "safety net for cases where cleanup fails", but this is too long and increases the attack surface if credentials are compromised.

Copilot uses AI. Check for mistakes.
Comment on lines +442 to +446
} catch (KuraException e) {
logger.error("Failed to create temporary identity for container {}", options.getContainerName(), e);
ContainerInstance.this.currentTemporaryIdentityName.set(null);
ContainerInstance.this.currentTemporaryToken.set(null);
}
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method catches all exceptions and sets both identity name and token to null. This could hide legitimate errors during identity creation. Consider logging the exception at error level before nulling out the values, or re-throwing certain types of exceptions that shouldn't be silently caught.

Copilot uses AI. Check for mistakes.
Comment on lines +370 to +382
private boolean isSamePassword(final String username, final String newPassword) {
try {
final Optional<PasswordHash> existingHash = this.identityHelper.getPasswordHash(username);
if (!existingHash.isPresent()) {
return false;
}

final PasswordHash newHash = this.identityHelper.computePasswordHash(newPassword.toCharArray());
return existingHash.get().equals(newHash);
} catch (final Exception e) {
return false;
}
}
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method isSamePassword compares password hashes by computing a new hash of the new password and comparing it with the existing hash. However, password hashes typically include a salt, and calling computePasswordHash with the same password twice will generate different hashes due to different salts. This comparison will always return false, making the check ineffective. The proper way is to verify the password against the existing hash using the password verification functionality.

Copilot uses AI. Check for mistakes.
Comment on lines +439 to +440
logger.info("Created temporary identity {} for container {} with {} permissions",
identityName, options.getContainerName(), permissions.size());
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Security concern: The temporary token is logged in plain text. Consider masking or redacting the token value in log messages to prevent exposure of credentials in log files.

Copilot uses AI. Check for mistakes.
.setDeviceList(baseConfig.getContainerDevices())
.setFrameworkManaged(baseConfig.isFrameworkManaged())
.setLoggingType(baseConfig.getContainerLoggingType())
.setContainerNetowrkConfiguration(baseConfig.getContainerNetworkConfiguration())
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in method name: setContainerNetowrkConfiguration should be setContainerNetworkConfiguration (missing 'r' in Network).

Suggested change
.setContainerNetowrkConfiguration(baseConfig.getContainerNetworkConfiguration())
.setContainerNetworkConfiguration(baseConfig.getContainerNetworkConfiguration())

Copilot uses AI. Check for mistakes.
Comment thread kura/org.eclipse.kura.container.provider/OSGI-INF/containerinstance.xml Outdated
.map(Permission::new)
.collect(Collectors.toSet());

final String identityName = "container_" + options.getContainerName().replace("-", "_");
Copy link

Copilot AI Jan 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing validation: The container name is converted from kebab-case to snake_case to create the identity name, but there's no validation that the resulting name is valid. If the container name contains special characters that aren't hyphens, the identity name could be invalid. Consider adding validation or sanitization of the identity name.

Suggested change
final String identityName = "container_" + options.getContainerName().replace("-", "_");
final String rawContainerName = options.getContainerName();
String sanitizedContainerName = rawContainerName == null ? "" : rawContainerName;
// Replace any character that is not a letter, digit or hyphen with underscore
sanitizedContainerName = sanitizedContainerName.replaceAll("[^A-Za-z0-9\\-]", "_");
// Preserve existing behavior of converting hyphens to underscores
sanitizedContainerName = sanitizedContainerName.replace("-", "_");
// Ensure we don't end up with an empty identity suffix
if (sanitizedContainerName.isEmpty()) {
sanitizedContainerName = "default";
}
final String identityName = "container_" + sanitizedContainerName;

Copilot uses AI. Check for mistakes.
@nicolatimeus
Copy link
Copy Markdown
Member

Password authentication currently does not work in the case when the user provides the password using the PasswordConfiguration.newPassword field

this.newPassword = requireNonNull(newPassword, "newPassword cannot be null");
.

In this case the identity service should compute the hash of the given password, and store that one in the passwordHash field, the newPassword fileld should not be populated in the PasswordConfigurations in the temporary store.

Part of the logic performed by the updateIdentityConfigurationInternal method (e.g. new password processing) should be done also in the case of a temporary identity.

Consider also further unifying the identity configuration validation logic

* with the given name already exists (either regular or temporary).
* @since 2.8.0
*/
public void createTemporaryIdentity(final String identityName, final IdentityConfiguration configuration,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The identityName argument is redundant since it is included in IdentityConfiguration, for symmetry with createIdentity() we could remove the configuration argument

@nicolatimeus
Copy link
Copy Markdown
Member

The implementation of the IdentityService is becoming more and more complicated since it contains different code paths for handling reguar and temporary identities. A possible approach for simplifying it could be creating a IdentityStore interface in the org.eclipse.kura.core.identity package with one implementation for temporary identities (can be based on the currently existing TemporaryIdentityStore) and another for the useradmin. The identity service could delegate the storage aspects of identity management to these implementation trying to keep a single code path for configuration validation and update.

* or if deletion fails.
* @since 2.8.0
*/
public boolean deleteTemporaryIdentity(final String identityName) throws KuraException;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it necessary to have a dedicated deleteTemporaryIdentity method or is it enough to extend the existing deleteIdentity method?

@MMaiero MMaiero force-pushed the feature/container-temporary-credentials branch from adfa91d to a5b4020 Compare January 26, 2026 17:02
@sonarqubecloud
Copy link
Copy Markdown

nicolatimeus
nicolatimeus previously approved these changes Jan 27, 2026
MMaiero and others added 11 commits February 7, 2026 18:16
Signed-off-by: MMaiero <matteo.maiero@eurotech.com>
Signed-off-by: MMaiero <matteo.maiero@eurotech.com>
Signed-off-by: MMaiero <matteo.maiero@eurotech.com>
Signed-off-by: Nicola Timeus <nicola.timeus@eurotech.com>
Signed-off-by: MMaiero <matteo.maiero@eurotech.com>
….eclipse.kura.container.provider.ContainerInstance.xml

Co-authored-by: nicolatimeus <nicola.timeus@eurotech.com>
@MMaiero MMaiero force-pushed the feature/container-temporary-credentials branch from b1c8a0c to cda9d08 Compare February 7, 2026 17:16
@MMaiero MMaiero requested a review from Copilot February 7, 2026 17:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 38 out of 38 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +452 to +456
final IdentityConfiguration configuration = new IdentityConfiguration(candidateName,
Arrays.asList(passwordConfiguration, assignedPermissions));

ContainerInstance.this.identityService.createTemporaryIdentity(candidateName, Duration.ofDays(365));
ContainerInstance.this.identityService.updateIdentityConfiguration(configuration);
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The temporary identity lifetime is hard-coded to Duration.ofDays(365). If Kura crashes or the cleanup path doesn’t run, the credentials can remain valid far longer than intended. Consider making the lifetime configurable and/or using a short default tied to container lifecycle (with renewal if needed).

Copilot uses AI. Check for mistakes.
Comment on lines +220 to +224
private void givenExistingIdentity() {
try {
this.identityService.createIdentity(this.identityName);
this.createdIdentities.add(this.identityName);
} catch (Exception e) {
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

givenExistingIdentity ignores the boolean return value from createIdentity. If the identity already exists, it still gets added to createdIdentities and will be deleted in @After, which can remove pre-existing identities. Only track identities for cleanup when createIdentity(...) returns true.

Copilot uses AI. Check for mistakes.
Comment on lines +129 to +133
configuration.update(configurationProperties);

} catch (Exception e) {
fail("cannot update configuration");
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The catch-all in givenConfiguration drops the underlying exception details, which makes test failures harder to debug. Include the exception message (or chain it) in the fail(...) message.

Copilot uses AI. Check for mistakes.
Comment on lines +234 to +235
* if the provided permissio is not currently assigned to
* the given identity or if occurs while performing the
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo in Javadoc: "permissio" → "permission".

Suggested change
* if the provided permissio is not currently assigned to
* the given identity or if occurs while performing the
* if the provided permission is not currently assigned to
* the given identity or if a failure occurs while performing the

Copilot uses AI. Check for mistakes.
if (options.isIdentityIntegrationEnabled() && password != null && identityName != null) {
final List<String> envVars = new ArrayList<>(baseConfig.getContainerEnvVars());
envVars.add("KURA_IDENTITY_NAME=" + identityName);
envVars.add("KURA_IDENTITY_PASSWORD=" + new String(password));
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

KURA_IDENTITY_PASSWORD is built from new String(password) which creates an extra immutable String copy of the secret in heap memory (cannot be wiped). Since you already keep the password as char[], consider only converting to String at the last possible moment (and avoid redundant copies), or pass it through in a way that minimizes additional String allocations.

Suggested change
envVars.add("KURA_IDENTITY_PASSWORD=" + new String(password));
final StringBuilder identityPasswordEnv = new StringBuilder("KURA_IDENTITY_PASSWORD=");
identityPasswordEnv.append(password);
envVars.add(identityPasswordEnv.toString());

Copilot uses AI. Check for mistakes.
Comment on lines +209 to +213
private void givenExistingPermissions(final String... permissionNames) {
for (String permissionName : permissionNames) {
try {
this.identityService.createPermission(new Permission(permissionName));
this.createdPermissions.add(permissionName);
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

givenExistingPermissions unconditionally adds permissions to createdPermissions even when createPermission returns false (already existed). The @After cleanup would then delete permissions that pre-existed, potentially breaking other tests/runs. Only add to createdPermissions when createPermission(...) returns true.

Copilot uses AI. Check for mistakes.
Comment on lines +993 to +997
private void givenTemporaryIdentity(final String name) {
callVoid(() -> {
this.identityService.createTemporaryIdentity(name, Duration.ofHours(1));
return null;
});
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

givenTemporaryIdentity uses callVoid, which records exceptions but doesn’t fail the test immediately. If temporary identity creation fails, the failure can be overwritten by the next call(...), allowing the test to pass with an invalid setup. Prefer doing this setup in a try/catch that calls fail(...) on error, or assert thenNoExceptionIsThrown() right after this setup step.

Copilot uses AI. Check for mistakes.
Comment on lines +147 to +152
for (int i = 0; i < 100; i++) {
final Response response = restTransport.runRequest("/ping", new MethodSpec("GET"));

if (response.getStatus() != 404 && response.getStatus() != 503) {
break;
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The service-startup wait loop breaks when the endpoint becomes available, but if it never becomes available the test proceeds without failing here (making failures later less diagnosable/flakier). Track whether startup succeeded and fail(...) after the loop if it didn’t.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 39 out of 39 changed files in this pull request and generated 20 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 130 to 140
try {
final User user = (User) this.userAdmin.getRole(KURA_USER_PREFIX + requestUser.getName());

return containsBasicMember(this.userAdmin.getRole(KURA_PERMISSION_REST_PREFIX + role), user)
|| containsBasicMember(this.userAdmin.getRole(KURA_PERMISSION_PREFIX + "kura.admin"), user);

this.identityService.checkPermission(requestUser.getName(), new Permission("rest." + role));
return true;
} catch (final Exception e) {
return false;
}
}

private static boolean containsBasicMember(final Role group, final User user) {
if (!(group instanceof Group)) {
return false;
}

final Group asGroup = (Group) group;

final Role[] members = asGroup.getMembers();

if (members == null) {
return false;
}

for (final Role member : members) {
if (member.getName().equals(user.getName())) {
try {
this.identityService.checkPermission(requestUser.getName(), new Permission(KURA_ADMIN_PERMISSION));
return true;
} catch (final Exception ignored) {
return false;
}
}
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The nested try-catch blocks on lines 130-140 for checking permissions have several issues. First, catching all Exceptions (line 133 and 137) is too broad and could hide unexpected errors. Second, the kura.admin permission check on line 135 is performed as a fallback for every permission check failure, which could mask genuine authorization issues and impact performance. Third, the exception 'ignored' on line 137 means authorization failures are silently suppressed. Consider logging at debug level when permission checks fail, and being more specific about which exceptions to catch (e.g., KuraException).

Copilot uses AI. Check for mistakes.
@@ -1,17 +1,20 @@
/*******************************************************************************
Copy link

Copilot AI Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR description is incomplete and lacks important details. It only contains the template structure without filling in the actual information about the changes, related issues, solution description, manual tests, or side notes. This makes it difficult to understand the context and scope of the changes without inspecting the code.

Copilot uses AI. Check for mistakes.
Comment thread kura/distrib/src/main/resources/filtered/pkg/install/kura.service
MMaiero and others added 3 commits February 24, 2026 08:04
…ipse/kura/container/provider/ContainerInstance.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ity/IdentityService.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…ity/IdentityService.java

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@MMaiero MMaiero merged commit 8ee22db into develop Feb 24, 2026
4 checks passed
@MMaiero MMaiero deleted the feature/container-temporary-credentials branch February 24, 2026 09:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants