Skip to content

Commit

Permalink
Merge branch 'develop' into issue-391-show-group-labels-in-account-ho…
Browse files Browse the repository at this point in the history
…me-page
  • Loading branch information
andreaceccanti authored Sep 1, 2021
2 parents 15f7f9f + 0014a27 commit 4b9560c
Show file tree
Hide file tree
Showing 74 changed files with 2,419 additions and 1,095 deletions.
51 changes: 51 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,55 @@
# Changelog


## 1.7.0 (2021-09-02)

### Added

- IAM now enforces intermediate group membership (#400)

- Support for X.509 managed proxies (#356)

- Characters allowed in username are now restricted to the UNIX valid username
characters (#347)

- Support for including custom HTML content at the bottom of the login page has
been added (#341)

- Improved token exchange flexibility (#306)

- CI has been migrated from travis to Github actions (#340)

- IAM now allows to link ssh keys to an account (#374)

### Fixed

- A problem that prevented the deletion of dynamically registered clients under
certains conditions has been fixed (#397)

- Token exchange is no longer allowed for single-client exchanges that involve
the `offline_access` scope (#392)

- More flexibility in populating registration fields from SAML authentication
assertion attributes (#371)

- A problem with the userinfo endpoint disclosing too much information has been
fixed (#348)

- A problem which allowed to submit multiple group requests for the same group
has been fixed (#351)

- A problem with the escaping of certificate subjects in the IAM dashboard has
been fixed (#373)

- A problem with the refresh of CRLs on the test client application has been
fixed (#368)

### Documentation

- The IAM website and documentation has been migrated to a site based on
[Google Docsy][docsy], including improved documentation for the SCIM, Scope
policy and Token exchange IAM APIs (#410)

## 1.6.0 (2020-07-31)

### Added
Expand Down Expand Up @@ -358,3 +408,4 @@ GitBook manual][gitbook-manual] or on [Github][github-doc].
[gitbook-manual]: https://www.gitbook.com/book/andreaceccanti/iam/details
[github-doc]: https://github.com/indigo-iam/iam/blob/master/SUMMARY.md
[jira-v0.4.0]: https://issues.infn.it/jira/browse/INDIAM/fixforversion/13811
[docsy]: https://github.com/google/docsy
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,6 @@ Grant number 777536.
[mitreid]: https://github.com/mitreid-connect/OpenID-Connect-Java-Spring-Server
[scim]: http://www.simplecloud.info/
[token-exchange]: https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-09
[iam-doc]: https://indigo-iam.github.io/docs
[iam-doc]: https://indigo-iam.github.io
[eosc-hub]: https://www.eosc-hub.eu/
[infn]: https://home.infn.it/it/
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package it.infn.mw.iam;

import org.mitre.discovery.web.DiscoveryEndpoint;
import org.mitre.openid.connect.web.JWKSetPublishingEndpoint;
import org.mitre.openid.connect.web.RootController;
import org.mitre.openid.connect.web.UserInfoEndpoint;
import org.springframework.boot.SpringApplication;
Expand Down Expand Up @@ -64,7 +65,9 @@
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,
value=DiscoveryEndpoint.class),
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,
value=HealthEndpoint.class)
value=HealthEndpoint.class),
@ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,
value=JWKSetPublishingEndpoint.class)
})
// @formatter:on

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,35 @@ private Supplier<IllegalArgumentException> groupNotFoundError(String groupNameOr
return () -> new IllegalArgumentException("Group does not exist: " + groupNameOrUuid);
}

@Override
public ScimListResponse<ScimUser> findAccountByCertificateSubject(String certSubject) {
Optional<IamAccount> account = repo.findByCertificateSubject(certSubject);
ScimListResponseBuilder<ScimUser> builder = ScimListResponse.builder();
account.ifPresent(a -> builder.singleResource(converter.dtoFromEntity(a)));
return builder.build();
}

@Override
public ScimListResponse<ScimUser> findAccountNotInGroup(String groupUuid,
Pageable pageable) {
IamGroup group = groupRepo.findByUuid(groupUuid).orElseThrow(groupNotFoundError(groupUuid));
Page<IamAccount> results = repo.findNotInGroup(group.getUuid(), pageable);
return responseFromPage(results, converter, pageable);
}

@Override
public ScimListResponse<ScimUser> findAccountNotInGroupWithFilter(String groupUuid, String filter,
Pageable pageable) {
IamGroup group = groupRepo.findByUuid(groupUuid).orElseThrow(groupNotFoundError(groupUuid));
Page<IamAccount> results = repo.findNotInGroupWithFilter(group.getUuid(), filter, pageable);
return responseFromPage(results, converter, pageable);
}

@Override
public ScimListResponse<ScimUser> findAccountByGroupUuidWithFilter(String groupUuid,
String filter, Pageable pageable) {
IamGroup group = groupRepo.findByUuid(groupUuid).orElseThrow(groupNotFoundError(groupUuid));
Page<IamAccount> results = repo.findByGroupUuidWithFilter(group.getUuid(), filter, pageable);
return responseFromPage(results, converter, pageable);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,38 @@
package it.infn.mw.iam.api.account.find;

import static it.infn.mw.iam.api.common.PagingUtils.buildPageRequest;
import static it.infn.mw.iam.api.utils.ValidationErrorUtils.handleValidationError;
import static java.util.Objects.isNull;
import static org.springframework.web.bind.annotation.RequestMethod.GET;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import it.infn.mw.iam.api.common.ListResponseDTO;
import it.infn.mw.iam.api.common.form.PaginatedRequestWithFilterForm;
import it.infn.mw.iam.api.scim.model.ScimConstants;
import it.infn.mw.iam.api.scim.model.ScimUser;

@RestController
@PreAuthorize("hasRole('ADMIN')")
public class FindAccountController {

public static final String INVALID_FIND_ACCOUNT_REQUEST = "Invalid find account request";

public static final String FIND_BY_LABEL_RESOURCE = "/iam/account/find/bylabel";
public static final String FIND_BY_EMAIL_RESOURCE = "/iam/account/find/byemail";
public static final String FIND_BY_USERNAME_RESOURCE = "/iam/account/find/byusername";
public static final String FIND_BY_CERT_SUBJECT_RESOURCE = "/iam/account/find/bycertsubject";
public static final String FIND_BY_GROUP_RESOURCE = "/iam/account/find/bygroup/{groupUuid}";
public static final String FIND_NOT_IN_GROUP_RESOURCE =
"/iam/account/find/notingroup/{groupUuid}";

final FindAccountService service;

Expand Down Expand Up @@ -65,4 +78,47 @@ public ListResponseDTO<ScimUser> findByUsername(@RequestParam(required = true) S
return service.findAccountByUsername(username);
}

@RequestMapping(method = GET, value = FIND_BY_CERT_SUBJECT_RESOURCE,
produces = ScimConstants.SCIM_CONTENT_TYPE)
public ListResponseDTO<ScimUser> findByCertSubject(
@RequestParam(required = true) String certificateSubject) {
return service.findAccountByCertificateSubject(certificateSubject);
}


@RequestMapping(method = GET, value = FIND_BY_GROUP_RESOURCE,
produces = ScimConstants.SCIM_CONTENT_TYPE)
public ListResponseDTO<ScimUser> findByGroup(@PathVariable String groupUuid,
@Validated PaginatedRequestWithFilterForm form,
BindingResult formValidationResult) {


handleValidationError(INVALID_FIND_ACCOUNT_REQUEST, formValidationResult);

Pageable pr = buildPageRequest(form.getCount(), form.getStartIndex(), 100);

if (isNull(form.getFilter())) {
return service.findAccountByGroupUuid(groupUuid, pr);
} else {
return service.findAccountByGroupUuidWithFilter(groupUuid, form.getFilter(), pr);
}
}


@RequestMapping(method = GET, value = FIND_NOT_IN_GROUP_RESOURCE,
produces = ScimConstants.SCIM_CONTENT_TYPE)
public ListResponseDTO<ScimUser> findNotInGroup(@PathVariable String groupUuid,
@Validated PaginatedRequestWithFilterForm form, BindingResult formValidationResult) {

handleValidationError(INVALID_FIND_ACCOUNT_REQUEST, formValidationResult);

Pageable pr = buildPageRequest(form.getCount(), form.getStartIndex(), 100);

if (isNull(form.getFilter())) {
return service.findAccountNotInGroup(groupUuid, pr);
} else {
return service.findAccountNotInGroupWithFilter(groupUuid, form.getFilter(), pr);
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,20 @@ public interface FindAccountService {

ScimListResponse<ScimUser> findInactiveAccounts(Pageable pageable);

ScimListResponse<ScimUser> findAccountByCertificateSubject(String certSubject);

ScimListResponse<ScimUser> findActiveAccounts(Pageable pageable);

ScimListResponse<ScimUser> findAccountByGroupName(String groupName, Pageable pageable);

ScimListResponse<ScimUser> findAccountByGroupUuid(String groupUuid, Pageable pageable);

ScimListResponse<ScimUser> findAccountByGroupUuidWithFilter(String groupUuid, String filter,
Pageable pageable);

ScimListResponse<ScimUser> findAccountNotInGroup(String groupUuid, Pageable pageable);

ScimListResponse<ScimUser> findAccountNotInGroupWithFilter(String groupUuid, String filter,
Pageable pageable);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2019
*
* 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.api.common.form;

import javax.validation.constraints.Min;

public class PaginatedRequestForm {

@Min(value = 0, message = "must be >=0")
private Integer count;

@Min(value = 1, message = "must be >=1")
private Integer startIndex;

public Integer getCount() {
return count;
}

public void setCount(Integer count) {
this.count = count;
}

public Integer getStartIndex() {
return startIndex;
}

public void setStartIndex(Integer startIndex) {
this.startIndex = startIndex;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2019
*
* 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.api.common.form;

import javax.validation.constraints.Size;

import it.infn.mw.iam.api.common.validator.NullableNonBlankString;

public class PaginatedRequestWithFilterForm extends PaginatedRequestForm {

@NullableNonBlankString(message = "Please provide a non-blank filter string")
@Size(min = 2, max = 64, message = "Please provide a filter that is between 2 and 64 chars long")
private String filter;

public String getFilter() {
return filter;
}

public void setFilter(String filter) {
this.filter = filter;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2019
*
* 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.api.common.validator;

import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Retention(RUNTIME)
@Target({FIELD, METHOD})
@Constraint(validatedBy = NullableNonBlankStringValidator.class)
public @interface NullableNonBlankString {

String message() default "The string, if not null, must not be blank";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) Istituto Nazionale di Fisica Nucleare (INFN). 2016-2019
*
* 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.api.common.validator;

import static java.util.Objects.isNull;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class NullableNonBlankStringValidator
implements ConstraintValidator<NullableNonBlankString, String> {

public NullableNonBlankStringValidator() {
}

@Override
public void initialize(NullableNonBlankString constraintAnnotation) {
}

@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return isNull(value) || value.trim().length() > 0;
}

}
Loading

0 comments on commit 4b9560c

Please sign in to comment.