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

AUP exemption #811

Open
wants to merge 37 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
22faeb3
Bump version to 1.11.0
enricovianello Oct 16, 2024
ae09f39
Self-service upload of user certificate (#750)
darcato Oct 16, 2024
aecb995
Revert "Self-service upload of user certificate (#750)"
enricovianello Nov 26, 2024
2777efe
Revert "Bump version to 1.11.0"
enricovianello Nov 26, 2024
6018c3e
Merge branch 'v1.10.3' into develop
enricovianello Nov 26, 2024
1e75841
Bump version to 1.11.0
enricovianello Nov 26, 2024
edb3d77
Set reset service account
garaimanoj Jul 19, 2024
4bfc038
Fix vomsAc test
garaimanoj Jul 22, 2024
fdfbac2
Fix typo and set service_account default to false
garaimanoj Jul 25, 2024
06cdacc
Test ServiceAccountReplacer works
garaimanoj Jul 25, 2024
d6efb95
Test ServiceAccountReplacedEvent is called
garaimanoj Jul 29, 2024
fff9c98
Throw error on AUP update for service account
garaimanoj Aug 6, 2024
7334fd4
Send email notification on service account status change
garaimanoj Aug 6, 2024
3ccbe00
Test email notification on service account status change
garaimanoj Aug 8, 2024
d675aa9
Test email notification on scim user provisioning method call
garaimanoj Aug 12, 2024
3852038
Remove the declaration of thrown exception
garaimanoj Aug 12, 2024
e955b0c
Unit test email notifications for service account operations
garaimanoj Aug 12, 2024
03e8c5d
Add license
garaimanoj Aug 12, 2024
8a9f6a8
Add SpringBootTest annotation
garaimanoj Aug 14, 2024
6c4d30b
Add unit test for service account emails message creation
garaimanoj Aug 14, 2024
ffb4319
Test notification delivery
garaimanoj Aug 15, 2024
3b9d9e0
Move test to notification test file
garaimanoj Aug 15, 2024
1aa1efd
Delete unnecessary tests
garaimanoj Aug 16, 2024
fb3905b
Move tests to registration flow test file to pass sonar cloud
garaimanoj Aug 16, 2024
011e3ee
Address review comments
garaimanoj Aug 19, 2024
04ac5bc
Add an ExceptionHandler for IamAupSignatureUpdateError
garaimanoj Aug 20, 2024
ec22e8c
Fix test
garaimanoj Aug 20, 2024
8a18dd7
Add exception handler in AupSignaturePageController
garaimanoj Aug 21, 2024
956330a
Block AUP reminder and expiration email for service account
garaimanoj Aug 22, 2024
5107bf0
Return error message as a JSON response
garaimanoj Sep 3, 2024
9a1f87f
Add serviceAccount field inside ScimIndigoUser extension object
garaimanoj Sep 18, 2024
b780971
Fix failing tests
garaimanoj Sep 19, 2024
0e8d554
Fix failing tests
garaimanoj Sep 19, 2024
fe9cfba
Remove getServiceAccount from ScimUser
garaimanoj Sep 20, 2024
e094baa
Move V106 migrations to V108
garaimanoj Oct 18, 2024
a48620e
Fix test
garaimanoj Oct 18, 2024
94695ea
Merge branch 'develop' into issue-737-aup-exemption
garaimanoj Jan 17, 2025
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 @@ -59,6 +59,12 @@ public boolean needsAupSignature(IamAccount account) {
return false;
}

if (account.isServiceAccount()) {
LOG.debug("AUP signature not needed for account '{}': Account is a service account",
account.getUsername());
return false;
}

if (isNull(account.getAupSignature())) {
LOG.debug("AUP signature needed for account '{}': no signature record found for user",
account.getUsername());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import it.infn.mw.iam.persistence.model.IamAupSignature;
import it.infn.mw.iam.persistence.repository.IamAupRepository;
import it.infn.mw.iam.persistence.repository.IamAupSignatureRepository;
import it.infn.mw.iam.persistence.repository.IamAupSignatureUpdateError;

@SuppressWarnings("deprecation")
@RestController
Expand Down Expand Up @@ -146,7 +147,7 @@ public AupSignatureDTO getSignatureForAccount(@PathVariable String accountId)
@PreAuthorize("#iam.hasScope('iam:admin.write') or #iam.hasDashboardRole('ROLE_ADMIN')")
public AupSignatureDTO updateSignatureForAccount(@PathVariable String accountId,
@RequestBody(required = false) @Validated AupSignaturePatchRequestDTO dto,
Authentication authentication) throws AccountNotFoundException {
Authentication authentication) throws AccountNotFoundException, IamAupSignatureUpdateError {

Optional<IamAccount> updaterAccount = accountUtils.getAuthenticatedUserAccount();

Expand Down Expand Up @@ -178,7 +179,7 @@ public AupSignatureDTO updateSignatureForAccount(@PathVariable String accountId,
@ResponseStatus(value = HttpStatus.NO_CONTENT)
@PreAuthorize("#iam.hasScope('iam:admin.write') or #iam.hasDashboardRole('ROLE_ADMIN')")
public void deleteSignatureForAccount(@PathVariable String accountId,
Authentication authentication) throws AccountNotFoundException {
Authentication authentication) throws AccountNotFoundException, IamAupSignatureUpdateError {

Optional<IamAccount> deleterAccount = accountUtils.getAuthenticatedUserAccount();
IamAccount signatureAccount = accountUtils.getByAccountId(accountId)
Expand Down Expand Up @@ -225,4 +226,10 @@ public ErrorDTO accountNotFoundError(Exception ex) {
public ErrorDTO aupNotFoundError(Exception ex) {
return ErrorDTO.fromString(ex.getMessage());
}

@ResponseStatus(value = HttpStatus.METHOD_NOT_ALLOWED)
@ExceptionHandler(IamAupSignatureUpdateError.class)
public ErrorDTO aupSignatureUpdateError(Exception ex) {
return ErrorDTO.fromString(ex.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,21 +28,26 @@
import javax.servlet.http.HttpSession;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.web.savedrequest.SavedRequest;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.ModelAndView;

import it.infn.mw.iam.api.account.AccountUtils;
import it.infn.mw.iam.api.common.ErrorDTO;
import it.infn.mw.iam.audit.events.aup.AupSignedEvent;
import it.infn.mw.iam.core.time.TimeProvider;
import it.infn.mw.iam.persistence.model.IamAccount;
import it.infn.mw.iam.persistence.model.IamAup;
import it.infn.mw.iam.persistence.model.IamAupSignature;
import it.infn.mw.iam.persistence.repository.IamAupRepository;
import it.infn.mw.iam.persistence.repository.IamAupSignatureRepository;
import it.infn.mw.iam.persistence.repository.IamAupSignatureUpdateError;

@Controller
public class AupSignaturePageController {
Expand Down Expand Up @@ -97,7 +102,7 @@ private Optional<SavedRequest> checkForSavedSpringSecurityRequest(HttpSession se
@PreAuthorize("hasRole('USER')")
@PostMapping(value = "/iam/aup/sign")
public ModelAndView signAup(HttpServletRequest request, HttpServletResponse response,
HttpSession session) {
HttpSession session) throws IamAupSignatureUpdateError {

Optional<IamAup> aup = repo.findDefaultAup();

Expand Down Expand Up @@ -126,6 +131,12 @@ public ModelAndView signAup(HttpServletRequest request, HttpServletResponse resp

return new ModelAndView("redirect:/dashboard");
}

@ExceptionHandler(IamAupSignatureUpdateError.class)
public ResponseEntity<ErrorDTO> aupSignatureUpdateError(Exception ex) {
ErrorDTO errorResponse = ErrorDTO.fromString(ex.getMessage());
return new ResponseEntity<>(errorResponse, HttpStatus.METHOD_NOT_ALLOWED);
}
}


Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public IamAccount entityFromDto(ScimUser scimUser) {
account.setActive(scimUser.getActive());
}

if (scimUser.hasServiceAccountStatus()) {
account.setServiceAccount(scimUser.getIndigoUser().getServiceAccount());
}

if (scimUser.getPassword() != null) {

account.setPassword(scimUser.getPassword());
Expand Down Expand Up @@ -190,6 +194,7 @@ public ScimUser dtoFromEntity(IamAccount entity) {
.meta(getScimMeta(entity))
.name(getScimName(entity))
.active(entity.isActive())
.serviceAccount(entity.isServiceAccount())
.displayName(entity.getUsername())
.locale(entity.getUserInfo().getLocale())
.nickName(entity.getUserInfo().getNickname())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,24 @@ public String toString() {
@Valid
private final List<ScimGroupRef> managedGroups;

private Boolean serviceAccount;

@JsonCreator
private ScimIndigoUser(@JsonProperty("oidcIds") List<ScimOidcId> oidcIds,
@JsonProperty("sshKeys") List<ScimSshKey> sshKeys,
@JsonProperty("samlIds") List<ScimSamlId> samlIds,
@JsonProperty("x509Certificates") List<ScimX509Certificate> certs,
@JsonProperty("aupSignatureTime") Date aupSignatureTime,
@JsonProperty("endTime") Date endTime) {
@JsonProperty("endTime") Date endTime,
@JsonProperty("serviceAccount") Boolean serviceAccount) {

this.oidcIds = oidcIds != null ? oidcIds : new LinkedList<>();
this.sshKeys = sshKeys != null ? sshKeys : new LinkedList<>();
this.samlIds = samlIds != null ? samlIds : new LinkedList<>();
this.certificates = certs != null ? certs : new LinkedList<>();
this.aupSignatureTime = aupSignatureTime;
this.endTime = endTime;
this.serviceAccount = serviceAccount;
this.labels = null;
this.authorities = null;
this.attributes = null;
Expand All @@ -110,6 +114,7 @@ private ScimIndigoUser(Builder b) {
this.certificates = b.certificates;
this.aupSignatureTime = b.aupSignatureTime;
this.endTime = b.endTime;
this.serviceAccount = b.serviceAccount;
this.labels = b.labels;
this.attributes = b.attributes;
this.managedGroups = b.managedGroups;
Expand Down Expand Up @@ -159,6 +164,10 @@ public Date getEndTime() {
return endTime;
}

public Boolean getServiceAccount() {
return serviceAccount;
}

public static Builder builder() {

return new Builder();
Expand All @@ -174,6 +183,7 @@ public static class Builder {

private Date aupSignatureTime;
private Date endTime;
private Boolean serviceAccount;

private List<String> authorities = Lists.newLinkedList();
private List<ScimAttribute> attributes = Lists.newLinkedList();
Expand Down Expand Up @@ -212,6 +222,11 @@ public Builder endTime(Date endTime) {
return this;
}

public Builder serviceAccount(Boolean serviceAccount) {
this.serviceAccount = serviceAccount;
return this;
}

public Builder labels(List<ScimLabel> labels) {
this.labels = labels;
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ private ScimUser(@JsonProperty("id") String id, @JsonProperty("externalId") Stri
@JsonProperty("userType") String userType,
@JsonProperty("preferredLanguage") String preferredLanguage,
@JsonProperty("locale") String locale, @JsonProperty("timezone") String timezone,
@JsonProperty("active") Boolean active, @JsonProperty("emails") List<ScimEmail> emails,
@JsonProperty("active") Boolean active,
@JsonProperty("emails") List<ScimEmail> emails,
@JsonProperty("addresses") List<ScimAddress> addresses,
@JsonProperty("photos") List<ScimPhoto> photos,
@JsonProperty("groups") Set<ScimGroupRef> groups,
Expand Down Expand Up @@ -215,7 +216,7 @@ public Boolean getActive() {

return active;
}

public List<ScimEmail> getEmails() {

return emails;
Expand Down Expand Up @@ -276,6 +277,11 @@ public boolean hasName() {
return name != null;
}

public boolean hasServiceAccountStatus() {

return indigoUser != null && indigoUser.getServiceAccount() != null;
}

public static Builder builder(String username) {

return new Builder(username);
Expand Down Expand Up @@ -465,6 +471,11 @@ public Builder endTime(Date endTime) {
return this;
}

public Builder serviceAccount(Boolean serviceAccount) {
indigoUserBuilder.serviceAccount(serviceAccount);
return this;
}

public Builder addAuthority(String authority) {

Preconditions.checkNotNull(authority, "Null authority");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_GIVEN_NAME;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_PASSWORD;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_PICTURE;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_SERVICE_ACCOUNT;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_USERNAME;

import java.util.ArrayList;
Expand Down Expand Up @@ -79,7 +80,8 @@ public class ScimUserProvisioning
ACCOUNT_ADD_SSH_KEY, ACCOUNT_REMOVE_SSH_KEY, ACCOUNT_ADD_X509_CERTIFICATE,
ACCOUNT_REMOVE_X509_CERTIFICATE, ACCOUNT_REPLACE_ACTIVE, ACCOUNT_REPLACE_EMAIL,
ACCOUNT_REPLACE_FAMILY_NAME, ACCOUNT_REPLACE_GIVEN_NAME, ACCOUNT_REPLACE_PASSWORD,
ACCOUNT_REPLACE_PICTURE, ACCOUNT_REPLACE_USERNAME, ACCOUNT_REMOVE_PICTURE);
ACCOUNT_REPLACE_PICTURE, ACCOUNT_REPLACE_USERNAME, ACCOUNT_REMOVE_PICTURE,
ACCOUNT_REPLACE_SERVICE_ACCOUNT);

private final IamAccountService accountService;
private final IamAccountRepository accountRepository;
Expand Down Expand Up @@ -283,6 +285,13 @@ private void handleSpecificUpdateType(IamAccount account, AccountUpdater u) {
notificationFactory.createAccountSuspendedMessage(account);
}
}
if (ACCOUNT_REPLACE_SERVICE_ACCOUNT.equals(u.getType())) {
if (account.isServiceAccount()) {
notificationFactory.createSetAsServiceAccountMessage(account);
} else {
notificationFactory.createRevokeServiceAccountMessage(account);
}
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public enum UpdaterType {
ACCOUNT_REPLACE_PICTURE("Replace user picture"),
ACCOUNT_REPLACE_USERNAME("Replace user username"),
ACCOUNT_REPLACE_ACTIVE("Replace user active status"),
ACCOUNT_REPLACE_SERVICE_ACCOUNT("Replace user service account status"),

ACCOUNT_ADD_OIDC_ID("Add OpenID Connect account to user"),
ACCOUNT_ADD_SAML_ID("Add SAML account to user"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package it.infn.mw.iam.api.scim.updater.builders;

import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_ACTIVE;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_SERVICE_ACCOUNT;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_EMAIL;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_FAMILY_NAME;
import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_GIVEN_NAME;
Expand All @@ -36,6 +37,7 @@
import it.infn.mw.iam.api.scim.updater.util.AccountFinder;
import it.infn.mw.iam.api.scim.updater.util.IdNotBoundChecker;
import it.infn.mw.iam.audit.events.account.ActiveReplacedEvent;
import it.infn.mw.iam.audit.events.account.ServiceAccountReplacedEvent;
import it.infn.mw.iam.audit.events.account.EmailReplacedEvent;
import it.infn.mw.iam.audit.events.account.FamilyNameReplacedEvent;
import it.infn.mw.iam.audit.events.account.GivenNameReplacedEvent;
Expand Down Expand Up @@ -146,4 +148,9 @@ public AccountUpdater active(boolean isActive) {
account::isActive, account::setActive, isActive, ActiveReplacedEvent::new);
}

public AccountUpdater serviceAccount(boolean isServiceAccount) {
return new DefaultAccountUpdater<Boolean, ServiceAccountReplacedEvent>(account, ACCOUNT_REPLACE_SERVICE_ACCOUNT,
account::isServiceAccount, account::setServiceAccount, isServiceAccount, ServiceAccountReplacedEvent::new);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,10 @@ private void prepareReplacers(List<AccountUpdater> updaters, ScimUser user, IamA
addUpdater(updaters, Objects::nonNull, user::getPassword, replace::password);
addUpdater(updaters, Objects::nonNull, user::getActive, replace::active);

if (user.hasServiceAccountStatus()) {
addUpdater(updaters, Objects::nonNull, user.getIndigoUser()::getServiceAccount, replace::serviceAccount);
}

if (user.hasEmails()) {
addUpdater(updaters, Objects::nonNull, user.getEmails().get(0)::getValue, replace::email);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* 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.account;

import static it.infn.mw.iam.api.scim.updater.UpdaterType.ACCOUNT_REPLACE_SERVICE_ACCOUNT;

import it.infn.mw.iam.api.scim.updater.UpdaterType;
import it.infn.mw.iam.persistence.model.IamAccount;

public class ServiceAccountReplacedEvent extends AccountUpdatedEvent {

private static final long serialVersionUID = 5681737929767602266L;

private final Boolean serviceAccount;

public ServiceAccountReplacedEvent(Object source, IamAccount account, Boolean serviceAccount) {
super(source, account, ACCOUNT_REPLACE_SERVICE_ACCOUNT, buildMessage(ACCOUNT_REPLACE_SERVICE_ACCOUNT, serviceAccount));
this.serviceAccount = serviceAccount;
}

public Boolean getServiceAccount() {
return serviceAccount;
}

protected static String buildMessage(UpdaterType t, Boolean serviceAccount) {
return String.format("%s: %s", t.getDescription(), serviceAccount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public void sendAupReminders() {

// check if an email of type AUP_EXPIRATION does not already exist, because it is never deleted
expiredSignatures.forEach(s -> {
if (isExpiredSignatureEmailNotAlreadySentFor(s.getAccount())) {
if (isExpiredSignatureEmailNotAlreadySentFor(s.getAccount()) && !s.getAccount().isServiceAccount()) {
notification.createAupSignatureExpMessage(s.getAccount());
}
});
Expand All @@ -88,7 +88,7 @@ private void processRemindersForInterval(IamAup aup, LocalDate currentDate, Inte

// check if an email of type AUP_REMINDER does not already exist, because it is never deleted
signatures.forEach(s -> {
if (isAupReminderEmailNotAlreadySentFor(s.getAccount(), tomorrowAsDate)) {
if (isAupReminderEmailNotAlreadySentFor(s.getAccount(), tomorrowAsDate) && !s.getAccount().isServiceAccount()) {
notification.createAupReminderMessage(s.getAccount(), aup);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,8 @@ IamEmailNotification createClientStatusChangedMessageFor(ClientDetailsEntity cli
IamEmailNotification createMfaDisableMessage(IamAccount account);

IamEmailNotification createMfaEnableMessage(IamAccount account);

IamEmailNotification createSetAsServiceAccountMessage(IamAccount account);

IamEmailNotification createRevokeServiceAccountMessage(IamAccount account);
}
Loading
Loading