Skip to content

Commit

Permalink
Merge branch 'feature/SHIBUI-1062' into feature/SHIBUI-1063
Browse files Browse the repository at this point in the history
  • Loading branch information
Bill Smith committed Jan 29, 2019
2 parents e83d8f8 + 1e7ad73 commit 84c0aac
Show file tree
Hide file tree
Showing 71 changed files with 4,376 additions and 6,366 deletions.
1 change: 1 addition & 0 deletions backend/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ dependencies {
compile 'io.springfox:springfox-swagger-ui:2.9.2'

testCompile "org.springframework.boot:spring-boot-starter-test"
testCompile "org.springframework.security:spring-security-test"
testCompile "org.spockframework:spock-core:1.1-groovy-2.4"
testCompile "org.spockframework:spock-spring:1.1-groovy-2.4"
testCompile "org.xmlunit:xmlunit-core:2.5.1"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,12 @@ class DevConfig {
emailAddress = 'peter@institution.edu'
roles.add(roleRepository.findByName('ROLE_USER').get())
it
}, new User().with {
}, new User().with { // allow us to auto-login as an admin
username = 'anonymousUser'
password = '{noop}anotheradmin'
password = '{noop}anonymous'
firstName = 'Anon'
lastName = 'Ymous'
emailAddress = 'anonymous@institution.edu'
emailAddress = 'anon@institution.edu'
roles.add(roleRepository.findByName('ROLE_ADMIN').get())
it
}]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class MetadataSourcesUiDefinitionController {
ResponseEntity<?> getUiDefinitionJsonSchema() {
try {
def parsedJson = jacksonObjectMapper.readValue(this.jsonSchemaLocation.url, Map)
jsonSchemaBuilderService.hideServiceEnabledFromNonAdmins(parsedJson)
jsonSchemaBuilderService.addReleaseAttributesToJson(parsedJson['properties']['attributeRelease']['widget'])
jsonSchemaBuilderService.addRelyingPartyOverridesToJson(parsedJson['properties']['relyingPartyOverrides'])
jsonSchemaBuilderService.addRelyingPartyOverridesCollectionDefinitionsToJson(parsedJson["definitions"])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package edu.internet2.tier.shibboleth.admin.ui.service

import edu.internet2.tier.shibboleth.admin.ui.configuration.CustomPropertiesConfiguration
import edu.internet2.tier.shibboleth.admin.ui.security.model.User
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService
import org.springframework.beans.factory.annotation.Autowired

/**
Expand All @@ -11,6 +13,12 @@ class JsonSchemaBuilderService {
@Autowired
CustomPropertiesConfiguration customPropertiesConfiguration

UserService userService

JsonSchemaBuilderService(UserService userService) {
this.userService = userService
}

void addReleaseAttributesToJson(Object json) {
json['data'] = customPropertiesConfiguration.getAttributes().collect {
[key: it['name'], label: it['displayName']]
Expand Down Expand Up @@ -62,4 +70,15 @@ class JsonSchemaBuilderService {
json[(String) it['name']] = definition
}
}

void hideServiceEnabledFromNonAdmins(Map json) {
User currentUser = userService.getCurrentUser()
if (currentUser != null && currentUser.role != 'ROLE_ADMIN') {
// user isn't an admin, so hide 'ServiceEnabled'
Map<String, String> serviceEnabled = (HashMap) json['properties']['serviceEnabled']
serviceEnabled['widget'] = 'hidden'
serviceEnabled.remove('title')
serviceEnabled.remove('description')
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
import edu.internet2.tier.shibboleth.admin.ui.scheduled.EntityDescriptorFilesScheduledTasks;
import edu.internet2.tier.shibboleth.admin.ui.scheduled.MetadataProvidersScheduledTasks;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserRoleService;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.service.DefaultMetadataResolversPositionOrderContainerService;
import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryService;
import edu.internet2.tier.shibboleth.admin.ui.service.DirectoryServiceImpl;
Expand Down Expand Up @@ -64,8 +65,8 @@ public EntityService jpaEntityService() {
}

@Bean
public EntityDescriptorService jpaEntityDescriptorService() {
return new JPAEntityDescriptorServiceImpl(openSamlObjects(), jpaEntityService());
public EntityDescriptorService jpaEntityDescriptorService(UserService userService) {
return new JPAEntityDescriptorServiceImpl(openSamlObjects(), jpaEntityService(), userService);
}

@Bean
Expand Down Expand Up @@ -198,7 +199,7 @@ public ModelRepresentationConversions modelRepresentationConversions() {
}

@Bean
public UserRoleService userRoleService(RoleRepository roleRepository) {
return new UserRoleService(roleRepository);
public UserService userService(RoleRepository roleRepository, UserRepository userRepository) {
return new UserService(roleRepository, userRepository);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package edu.internet2.tier.shibboleth.admin.ui.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;

/**
* @author Bill Smith (wsmith@unicon.net)
*/
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class EndpointSecurityConfiguration extends GlobalMethodSecurityConfiguration {
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import edu.internet2.tier.shibboleth.admin.ui.jsonschema.JsonSchemaResourceLocationRegistry;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.service.JsonSchemaBuilderService;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
Expand Down Expand Up @@ -96,7 +99,7 @@ public JsonSchemaResourceLocationRegistry jsonSchemaResourceLocationRegistry(Res
}

@Bean
public JsonSchemaBuilderService jsonSchemaBuilderService() {
return new JsonSchemaBuilderService();
public JsonSchemaBuilderService jsonSchemaBuilderService(UserService userService) {
return new JsonSchemaBuilderService(userService);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
import edu.internet2.tier.shibboleth.admin.ui.domain.frontend.EntityDescriptorRepresentation;
import edu.internet2.tier.shibboleth.admin.ui.opensaml.OpenSamlObjects;
import edu.internet2.tier.shibboleth.admin.ui.repository.EntityDescriptorRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.model.User;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.RoleRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.repository.UserRepository;
import edu.internet2.tier.shibboleth.admin.ui.security.service.UserService;
import edu.internet2.tier.shibboleth.admin.ui.service.EntityDescriptorService;
import org.opensaml.core.xml.io.MarshallingException;
import org.slf4j.Logger;
Expand Down Expand Up @@ -48,10 +52,22 @@ public class EntityDescriptorController {
@Autowired
RestTemplateBuilder restTemplateBuilder;

private UserRepository userRepository;

private RoleRepository roleRepository;

private UserService userService;

private RestTemplate restTemplate;

private static Logger LOGGER = LoggerFactory.getLogger(EntityDescriptorController.class);

public EntityDescriptorController(UserRepository userRepository, RoleRepository roleRepository, UserService userService) {
this.userRepository = userRepository;
this.roleRepository = roleRepository;
this.userService = userService;
}

@PostConstruct
public void initRestTemplate() {
this.restTemplate = restTemplateBuilder.build();
Expand All @@ -61,6 +77,11 @@ public void initRestTemplate() {
public ResponseEntity<?> create(@RequestBody EntityDescriptorRepresentation edRepresentation) {
final String entityId = edRepresentation.getEntityId();

ResponseEntity<?> entityDescriptorEnablingDeniedResponse = entityDescriptorEnablePermissionsCheck(edRepresentation.isServiceEnabled());
if (entityDescriptorEnablingDeniedResponse != null) {
return entityDescriptorEnablingDeniedResponse;
}

ResponseEntity<?> existingEntityDescriptorConflictResponse = existingEntityDescriptorCheck(entityId);
if (existingEntityDescriptorConflictResponse != null) {
return existingEntityDescriptorConflictResponse;
Expand Down Expand Up @@ -95,56 +116,90 @@ public ResponseEntity<?> upload(@RequestParam String metadataUrl, @RequestParam

@PutMapping("/EntityDescriptor/{resourceId}")
public ResponseEntity<?> update(@RequestBody EntityDescriptorRepresentation edRepresentation, @PathVariable String resourceId) {
User currentUser = userService.getCurrentUser();
EntityDescriptor existingEd = entityDescriptorRepository.findByResourceId(resourceId);
if (existingEd == null) {
return ResponseEntity.notFound().build();
} else {
if (currentUser != null && (currentUser.getRole().equals("ROLE_ADMIN") || currentUser.getUsername().equals(existingEd.getCreatedBy()))) {
ResponseEntity<?> entityDescriptorEnablingDeniedResponse = entityDescriptorEnablePermissionsCheck(edRepresentation.isServiceEnabled());
if (entityDescriptorEnablingDeniedResponse != null) {
return entityDescriptorEnablingDeniedResponse;
}

// Verify we're the only one attempting to update the EntityDescriptor
if (edRepresentation.getVersion() != existingEd.hashCode()) {
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}

EntityDescriptor updatedEd =
EntityDescriptor.class.cast(entityDescriptorService.createDescriptorFromRepresentation(edRepresentation));

updatedEd.setAudId(existingEd.getAudId());
updatedEd.setResourceId(existingEd.getResourceId());
updatedEd.setCreatedDate(existingEd.getCreatedDate());

updatedEd = entityDescriptorRepository.save(updatedEd);

return ResponseEntity.ok().body(entityDescriptorService.createRepresentationFromDescriptor(updatedEd));
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN,
"You are not authorized to perform the requested operation."));
}
}

// Verify we're the only one attempting to update the EntityDescriptor
if (edRepresentation.getVersion() != existingEd.hashCode()) {
return new ResponseEntity<Void>(HttpStatus.CONFLICT);
}

EntityDescriptor updatedEd =
EntityDescriptor.class.cast(entityDescriptorService.createDescriptorFromRepresentation(edRepresentation));

updatedEd.setAudId(existingEd.getAudId());
updatedEd.setResourceId(existingEd.getResourceId());
updatedEd.setCreatedDate(existingEd.getCreatedDate());

updatedEd = entityDescriptorRepository.save(updatedEd);

return ResponseEntity.ok().body(entityDescriptorService.createRepresentationFromDescriptor(updatedEd));
}

@GetMapping("/EntityDescriptors")
@Transactional(readOnly = true)
public Iterable<EntityDescriptorRepresentation> getAll() {
return entityDescriptorRepository.findAllByCustomQueryAndStream()
.map(ed -> entityDescriptorService.createRepresentationFromDescriptor(ed))
.collect(Collectors.toList());
public ResponseEntity<?> getAll() {
User currentUser = userService.getCurrentUser();
if (currentUser != null) {
if (currentUser.getRole().equals("ROLE_ADMIN")) {
return ResponseEntity.ok(entityDescriptorRepository.findAllStreamByCustomQuery()
.map(ed -> entityDescriptorService.createRepresentationFromDescriptor(ed))
.collect(Collectors.toList()));
} else {
return ResponseEntity.ok(entityDescriptorRepository.findAllStreamByCreatedBy(currentUser.getUsername())
.map(ed -> entityDescriptorService.createRepresentationFromDescriptor(ed))
.collect(Collectors.toList()));
}
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN,
"You are not authorized to perform the requested operation."));
}
}

@GetMapping("/EntityDescriptor/{resourceId}")
public ResponseEntity<?> getOne(@PathVariable String resourceId) {
User currentUser = userService.getCurrentUser();
EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId);
if (ed == null) {
return ResponseEntity.notFound().build();
} else {
if (currentUser != null && (currentUser.getRole().equals("ROLE_ADMIN") || currentUser.getUsername().equals(ed.getCreatedBy()))) {
EntityDescriptorRepresentation edr = entityDescriptorService.createRepresentationFromDescriptor(ed);
return ResponseEntity.ok(edr);
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(new ErrorResponse(HttpStatus.FORBIDDEN,
"You are not authorized to perform the requested operation."));
}
}
EntityDescriptorRepresentation edr = entityDescriptorService.createRepresentationFromDescriptor(ed);

return ResponseEntity.ok(edr);
}

@GetMapping(value = "/EntityDescriptor/{resourceId}", produces = "application/xml")
public ResponseEntity<?> getOneXml(@PathVariable String resourceId) throws MarshallingException {
User currentUser = userService.getCurrentUser();
EntityDescriptor ed = entityDescriptorRepository.findByResourceId(resourceId);
if (ed == null) {
return ResponseEntity.notFound().build();
} else {
if (currentUser != null && (currentUser.getRole().equals("ROLE_ADMIN") || currentUser.getUsername().equals(ed.getCreatedBy()))) {
final String xml = this.openSamlObjects.marshalToXmlString(ed);
return ResponseEntity.ok(xml);
} else {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
}
final String xml = this.openSamlObjects.marshalToXmlString(ed);

return ResponseEntity.ok(xml);
}

@Transactional
Expand Down Expand Up @@ -191,6 +246,17 @@ private ResponseEntity<?> existingEntityDescriptorCheck(String entityId) {
return null;
}

private ResponseEntity<?> entityDescriptorEnablePermissionsCheck(boolean serviceEnabled) {
User user = userService.getCurrentUser();
if (user != null) {
if (serviceEnabled && !user.getRole().equals("ROLE_ADMIN")) {
return ResponseEntity.status(HttpStatus.FORBIDDEN)
.body(new ErrorResponse(HttpStatus.FORBIDDEN, "You do not have the permissions necessary to enable this service."));
}
}
return null;
}

private ResponseEntity<?> handleUploadingEntityDescriptorXml(byte[] rawXmlBytes, String spName) throws Exception {
final EntityDescriptor ed = EntityDescriptor.class.cast(openSamlObjects.unmarshalFromXml(rawXmlBytes));

Expand All @@ -204,4 +270,5 @@ private ResponseEntity<?> handleUploadingEntityDescriptorXml(byte[] rawXmlBytes,
return ResponseEntity.created(getResourceUriFor(persistedEd))
.body(entityDescriptorService.createRepresentationFromDescriptor(persistedEd));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,6 @@

public class EntityDescriptorRepresentation implements Serializable {


private int version;

public EntityDescriptorRepresentation() {
}

Expand Down Expand Up @@ -63,6 +60,10 @@ public EntityDescriptorRepresentation(String id,

private List<String> attributeRelease;

private int version;

private String createdBy;

public String getId() {
return id;
}
Expand Down Expand Up @@ -204,4 +205,12 @@ public int getVersion() {
public void setVersion(int version) {
this.version = version;
}

public String getCreatedBy() {
return createdBy;
}

public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ public interface EntityDescriptorRepository extends JpaRepository<EntityDescript

EntityDescriptor findByResourceId(String resourceId);

Stream<EntityDescriptor> findAllByServiceEnabled(boolean serviceEnabled);
Stream<EntityDescriptor> findAllStreamByServiceEnabled(boolean serviceEnabled);

@Query("select e from EntityDescriptor e")
Stream<EntityDescriptor> findAllByCustomQueryAndStream();
Stream<EntityDescriptor> findAllStreamByCustomQuery();

@Query("select e from EntityDescriptor e, User u join u.roles r " +
"where e.createdBy = u.username and e.serviceEnabled = false and r.name in ('ROLE_USER', 'ROLE_NONE')")
Stream<EntityDescriptor> findAllDisabledAndNotOwnedByAdmin();

Stream<EntityDescriptor> findAllStreamByCreatedBy(String createdBy);
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public EntityDescriptorFilesScheduledTasks(String metadataDirName,
@Scheduled(fixedRateString = "${shibui.taskRunRate:30000}")
@Transactional(readOnly = true)
public void generateEntityDescriptorFiles() throws MarshallingException {
this.entityDescriptorRepository.findAllByServiceEnabled(true)
this.entityDescriptorRepository.findAllStreamByServiceEnabled(true)
.forEach(ed -> {
Path targetFilePath = targetFilePathFor(toSha1HexString(ed.getEntityID()));
if (Files.exists(targetFilePath)) {
Expand Down Expand Up @@ -91,7 +91,7 @@ public void removeDanglingEntityDescriptorFiles() {
.map(it -> it.substring(0, it.indexOf(".")))
.collect(toSet());

Set<String> enabledEidsSha1Hashes = this.entityDescriptorRepository.findAllByServiceEnabled(true)
Set<String> enabledEidsSha1Hashes = this.entityDescriptorRepository.findAllStreamByServiceEnabled(true)
.map(EntityDescriptor::getEntityID)
.map(EntityDescriptorFilesScheduledTasks::toSha1HexString)
.collect(toSet());
Expand Down
Loading

0 comments on commit 84c0aac

Please sign in to comment.