Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Highlight how long a client has not been used #743

Merged
merged 40 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
48207cb
Add db migration to add LAST_USED column on IAM_ACCOUNT_CLIENT
darcato Nov 8, 2023
67d931c
Revert "Add db migration to add LAST_USED column on IAM_ACCOUNT_CLIENT"
darcato Nov 15, 2023
43fcad8
Adding table CLIENT_LAST_USED
darcato Nov 17, 2023
afbfc8d
Adding last used column on clients table on webapp
darcato Nov 17, 2023
087a727
Adding Client Last Used entity and repository
darcato Nov 21, 2023
ee34643
Simple GET api
darcato Nov 27, 2023
8434312
Fix repo type
darcato Nov 28, 2023
051f995
Reading last_used as ClientDetails column
darcato Dec 4, 2023
65dc285
Update frontend
darcato Dec 4, 2023
a05178d
Updating client last_used when generating access token
darcato Dec 11, 2023
1304ca4
Last used on refreshAccessToken
darcato Dec 11, 2023
f559a2b
Update mitreid.version to 1.3.6.iam-issue-605
darcato Dec 12, 2023
293e391
Add TOKEN event to IamAuditApplicationEvent and publish AccessTokenIs…
darcato Dec 12, 2023
02e48cd
Header
darcato Dec 12, 2023
0c63fc0
Add updateLastUsed methods to ClientService
darcato Dec 15, 2023
6240890
Remove update of last_used on client update
darcato Dec 19, 2023
8c4cc07
Adding mysql migration
darcato Dec 19, 2023
0e3ddb5
Save client on update of last_used
darcato Dec 19, 2023
89d2337
Do not save on clientRepo when updating last_used
darcato Dec 20, 2023
361e31a
Do not write to DB when same date
darcato Dec 20, 2023
ff5d243
Adding IAM_CLIENT_TRACK_LAST_USED env variable
darcato Dec 20, 2023
8654cf7
Remove unused clientRepo
darcato Jan 16, 2024
dc805a3
Suppress deprecation warnings
darcato Jan 16, 2024
49bf09f
Move LAST_USED to its own table
darcato Jan 24, 2024
116a61f
Add missing mysql migration
darcato Jan 24, 2024
a32acbd
Rename client_last_used and last_used_id
darcato Jan 31, 2024
e122465
Rename ClientLastUsedEntity and use LocalDate
darcato Jan 31, 2024
9301c92
Serialize LocalDate as String
darcato Jan 31, 2024
e696a97
Use a shared primary key
darcato Jan 31, 2024
94b9d48
Bump migration from V97 to V100 after rebase
darcato Feb 2, 2024
8b12aad
Remove unused import
darcato Feb 2, 2024
9923c77
Add first test
darcato Feb 2, 2024
04b6eca
Update MitreID version
darcato Feb 6, 2024
03e433f
Add more tests
darcato Feb 6, 2024
23c7d43
Refactor tests with TestTokenUtils
darcato Feb 7, 2024
5e6c8ad
Use final version of MitreID
darcato Feb 7, 2024
eb739b3
Change assertThat with assertTrue
darcato Feb 7, 2024
026a93a
Hide column on dashboard when tracking disabled
darcato Feb 15, 2024
b28228a
Enable the tracking by default
darcato Feb 15, 2024
f7802fa
Update migrations to latest available number
enricovianello Apr 10, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ public ClientDetailsEntity entityFromClientManagementRequest(RegisteredClientDTO

public RegisteredClientDTO registeredClientDtoFromEntity(ClientDetailsEntity entity) {
RegisteredClientDTO clientDTO = new RegisteredClientDTO();

clientDTO.setClientId(entity.getClientId());
clientDTO.setClientSecret(entity.getClientSecret());
clientDTO.setClientName(entity.getClientName());
Expand All @@ -127,6 +127,9 @@ public RegisteredClientDTO registeredClientDtoFromEntity(ClientDetailsEntity ent
clientDTO.setTosUri(entity.getTosUri());

clientDTO.setCreatedAt(entity.getCreatedAt());
if (entity.getClientLastUsed() != null) {
clientDTO.setLastUsed(entity.getClientLastUsed().getLastUsed());
}
clientDTO.setAccessTokenValiditySeconds(entity.getAccessTokenValiditySeconds());
clientDTO.setAllowIntrospection(entity.isAllowIntrospection());
clientDTO.setClearAccessTokensOnRefresh(entity.isClearAccessTokensOnRefresh());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,4 @@ Optional<ClientDetailsEntity> findClientByClientIdAndAccount(String clientId,
ClientDetailsEntity updateClient(ClientDetailsEntity client);

void deleteClient(ClientDetailsEntity client);

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package it.infn.mw.iam.api.common.client;

import java.time.LocalDate;
import java.util.Date;
import java.util.Set;

Expand All @@ -29,6 +30,7 @@

import org.hibernate.validator.constraints.URL;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
Expand Down Expand Up @@ -234,6 +236,11 @@ public class RegisteredClientDTO {
ClientViews.DynamicRegistration.class})
private Date createdAt;

@JsonView({ClientViews.Limited.class, ClientViews.Full.class, ClientViews.ClientManagement.class,
ClientViews.DynamicRegistration.class})
@JsonFormat(shape = JsonFormat.Shape.STRING)
private LocalDate lastUsed;

@JsonView({ClientViews.Full.class, ClientViews.ClientManagement.class,
ClientViews.DynamicRegistration.class})
@Size(max = 2048, groups = {OnClientCreation.class, OnClientUpdate.class})
Expand Down Expand Up @@ -472,6 +479,14 @@ public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}

public LocalDate getLastUsed() {
return lastUsed;
}

public void setLastUsed(LocalDate lastUsed) {
this.lastUsed = lastUsed;
}

public String getJwk() {
return jwk;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ public enum IamEventCategory {
SCOPE_POLICY,
AUP,
MEMBERSHIP,
CLIENT
CLIENT,
TOKEN
}

private static final long serialVersionUID = -6276169409979227109L;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.audit.events.tokens;

import org.mitre.oauth2.model.OAuth2AccessTokenEntity;


public class AccessTokenIssuedEvent extends TokenEvent {

private static final long serialVersionUID = 1L;

public AccessTokenIssuedEvent(Object source, OAuth2AccessTokenEntity token) {
super(source, token, "Access token issued");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.audit.events.tokens;

import org.mitre.oauth2.model.OAuth2AccessTokenEntity;


public class AccessTokenRefreshedEvent extends TokenEvent {

private static final long serialVersionUID = 1L;

public AccessTokenRefreshedEvent(Object source, OAuth2AccessTokenEntity token) {
super(source, token, "Access token refreshed");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2021
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package it.infn.mw.iam.audit.events.tokens;

import java.util.Date;
import java.util.Set;

import org.mitre.oauth2.model.OAuth2AccessTokenEntity;

import it.infn.mw.iam.audit.events.IamAuditApplicationEvent;

public abstract class TokenEvent extends IamAuditApplicationEvent {
private static final long serialVersionUID = 1L;
private final Date expiration;
private final String clientId;
private final Set<String> scopes;

public TokenEvent(Object source, OAuth2AccessTokenEntity token, String message) {
super(IamEventCategory.TOKEN, source, message);
this.expiration = token.getExpiration();
this.clientId = token.getClient().getClientId();
this.scopes = token.getScope();
}

public Date getExpiration() {
return expiration;
}

public String getClientId() {
return clientId;
}

public Set<String> getScopes() {
return scopes;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,13 @@ public void setPassword(String password) {

}


public static class ExternalConnectivityProbeProperties {

private boolean enabled = true;

private String endpoint = "https://www.google.com";
private int timeoutInSecs = 10;


public boolean isEnabled() {
return enabled;
}
Expand Down Expand Up @@ -292,7 +290,6 @@ public void setAllowCompleteVerificationUri(Boolean allowCompleteVerificationUri
this.allowCompleteVerificationUri = allowCompleteVerificationUri;
}


}

public static class JWKProperties {
Expand Down Expand Up @@ -540,6 +537,18 @@ public void setLocation(String location) {
}
}

public static class ClientProperties {
private boolean trackLastUsed;

public boolean isTrackLastUsed() {
return trackLastUsed;
}

public void setTrackLastUsed(boolean trackLastUsed) {
this.trackLastUsed = trackLastUsed;
}
}

private String host;

private String issuer;
Expand Down Expand Up @@ -588,14 +597,14 @@ public void setLocation(String location) {

private CustomizationProperties customization = new CustomizationProperties();

private VersionedStaticResourcesProperties versionedStaticResources =
new VersionedStaticResourcesProperties();
private VersionedStaticResourcesProperties versionedStaticResources = new VersionedStaticResourcesProperties();

private ExternalConnectivityProbeProperties externalConnectivityProbe =
new ExternalConnectivityProbeProperties();
private ExternalConnectivityProbeProperties externalConnectivityProbe = new ExternalConnectivityProbeProperties();

private AccountLinkingProperties accountLinking = new AccountLinkingProperties();

private ClientProperties client = new ClientProperties();

public String getBaseUrl() {
return baseUrl;
}
Expand Down Expand Up @@ -814,4 +823,12 @@ public void setAccountLinking(AccountLinkingProperties accountLinking) {
this.accountLinking = accountLinking;
}

public void setClient(ClientProperties client) {
this.client = client;
}

public ClientProperties getClient(){
return client;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,26 @@
import java.util.Date;
import java.util.Set;

import java.time.LocalDate;
import org.mitre.oauth2.model.ClientDetailsEntity;
import org.mitre.oauth2.model.ClientLastUsedEntity;
import org.mitre.oauth2.model.OAuth2AccessTokenEntity;
import org.mitre.oauth2.model.OAuth2RefreshTokenEntity;
import org.mitre.oauth2.service.impl.DefaultOAuth2ProviderTokenService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Primary;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.TokenRequest;
import org.springframework.stereotype.Service;

import com.google.common.collect.Sets;

import it.infn.mw.iam.audit.events.tokens.AccessTokenIssuedEvent;
import it.infn.mw.iam.audit.events.tokens.AccessTokenRefreshedEvent;
import it.infn.mw.iam.config.IamProperties;
import it.infn.mw.iam.persistence.repository.IamOAuthAccessTokenRepository;
import it.infn.mw.iam.persistence.repository.IamOAuthRefreshTokenRepository;

Expand All @@ -40,14 +49,19 @@ public class IamTokenService extends DefaultOAuth2ProviderTokenService {

private final IamOAuthAccessTokenRepository accessTokenRepo;
private final IamOAuthRefreshTokenRepository refreshTokenRepo;
private final ApplicationEventPublisher eventPublisher;
private final IamProperties iamProperties;


@Autowired
public IamTokenService(IamOAuthAccessTokenRepository atRepo,
IamOAuthRefreshTokenRepository rtRepo) {
IamOAuthRefreshTokenRepository rtRepo, ApplicationEventPublisher publisher,
IamProperties iamProperties) {

this.accessTokenRepo = atRepo;
this.refreshTokenRepo = rtRepo;
this.eventPublisher = publisher;
this.iamProperties = iamProperties;
}

@Override
Expand Down Expand Up @@ -76,4 +90,48 @@ public void revokeRefreshToken(OAuth2RefreshTokenEntity refreshToken) {
refreshTokenRepo.delete(refreshToken);
}

@Override
@SuppressWarnings("deprecation")
public OAuth2AccessTokenEntity createAccessToken(OAuth2Authentication authentication) {

OAuth2AccessTokenEntity token = super.createAccessToken(authentication);

if (iamProperties.getClient().isTrackLastUsed()) {
updateClientLastUsed(token);
}

eventPublisher.publishEvent(new AccessTokenIssuedEvent(this, token));
return token;
}

@Override
@SuppressWarnings("deprecation")
public OAuth2AccessTokenEntity refreshAccessToken(String refreshTokenValue,
TokenRequest authRequest) {

OAuth2AccessTokenEntity token = super.refreshAccessToken(refreshTokenValue, authRequest);

if (iamProperties.getClient().isTrackLastUsed()) {
updateClientLastUsed(token);
}

eventPublisher.publishEvent(new AccessTokenRefreshedEvent(this, token));
return token;
}

private void updateClientLastUsed(OAuth2AccessTokenEntity token) {
ClientDetailsEntity client = token.getClient();
ClientLastUsedEntity clientLastUsed = client.getClientLastUsed();
LocalDate now = LocalDate.now();

if (clientLastUsed == null) {
clientLastUsed = new ClientLastUsedEntity(client, now);
client.setClientLastUsed(clientLastUsed);
} else {
LocalDate lastUsed = clientLastUsed.getLastUsed();
if (lastUsed.isBefore(now)) {
clientLastUsed.setLastUsed(now);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class IamViewInfoInterceptor implements HandlerInterceptor {
public static final String RCAUTH_ENABLED_KEY = "iamRcauthEnabled";
public static final String RESOURCES_PATH_KEY = "resourcesPrefix";
public static final String CLIENT_DEFAULTS_PROPERTIES_KEY = "clientDefaultsProperties";
public static final String CLIENT_TRACK_LAST_USED_KEY = "clientTrackLastUsed";

@Value("${iam.version}")
String iamVersion;
Expand Down Expand Up @@ -83,6 +84,8 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons
request.setAttribute(RCAUTH_ENABLED_KEY, rcAuthProperties.isEnabled());

request.setAttribute(CLIENT_DEFAULTS_PROPERTIES_KEY, clientRegistrationProperties.getClientDefaults());

request.setAttribute(CLIENT_TRACK_LAST_USED_KEY, iamProperties.getClient().isTrackLastUsed());

if (iamProperties.getVersionedStaticResources().isEnableVersioning()) {
request.setAttribute(RESOURCES_PATH_KEY, String.format("/resources/%s", gitCommitId));
Expand Down
3 changes: 3 additions & 0 deletions iam-login-service/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,9 @@ client-registration:
default-id-token-validity-seconds: ${DEFAULT_ID_TOKEN_VALIDITY_SECONDS:600}
default-refresh-token-validity-seconds: ${DEFAULT_REFRESH_TOKEN_VALIDITY_SECONDS:2592000}

client:
track-last-used: ${IAM_CLIENT_TRACK_LAST_USED:true}

management:
health:
redis:
Expand Down
4 changes: 4 additions & 0 deletions iam-login-service/src/main/webapp/WEB-INF/tags/iamHeader.tag
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,8 @@ function getAccessTokenValiditySeconds() {
function getRefreshTokenValiditySeconds() {
return ${clientDefaultsProperties.defaultRefreshTokenValiditySeconds};
}

function getClientTrackLastUsed() {
return ${clientTrackLastUsed};
}
</script>
Loading
Loading