package de.passau.uni.sec.compose.id.core.service;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.passau.uni.sec.compose.id.common.exception.IdManagementException;
import de.passau.uni.sec.compose.id.common.exception.IdManagementException.Level;
import de.passau.uni.sec.compose.id.core.domain.ComposeUserPrincipal;
import de.passau.uni.sec.compose.id.core.domain.IPrincipal;
import de.passau.uni.sec.compose.id.core.event.ApproveAttributeValueEvent;
import de.passau.uni.sec.compose.id.core.event.CreateAttributeDefinitionEvent;
import de.passau.uni.sec.compose.id.core.event.CreateAttributeValueEvent;
import de.passau.uni.sec.compose.id.core.event.DetailsIdEvent;
import de.passau.uni.sec.compose.id.core.event.Event;
import de.passau.uni.sec.compose.id.core.event.ListAttributesInGroupEvent;
import de.passau.uni.sec.compose.id.core.event.ListPendingAttributeValueEvent;
import de.passau.uni.sec.compose.id.core.event.UpdateAttributeValueEvent;
import de.passau.uni.sec.compose.id.core.persistence.entities.AbstractMultiInstanceRelationship;
import de.passau.uni.sec.compose.id.core.persistence.entities.Application;
import de.passau.uni.sec.compose.id.core.persistence.entities.AttributeDefinition;
import de.passau.uni.sec.compose.id.core.persistence.entities.AttributeValue;
import de.passau.uni.sec.compose.id.core.persistence.entities.CoreEntity;
import de.passau.uni.sec.compose.id.core.persistence.entities.Group;
import de.passau.uni.sec.compose.id.core.persistence.entities.IEntity;
import de.passau.uni.sec.compose.id.core.persistence.entities.Membership;
import de.passau.uni.sec.compose.id.core.persistence.entities.ServiceComposition;
import de.passau.uni.sec.compose.id.core.persistence.entities.ServiceInstance;
import de.passau.uni.sec.compose.id.core.persistence.entities.ServiceObject;
import de.passau.uni.sec.compose.id.core.persistence.entities.ServiceSourceCode;
import de.passau.uni.sec.compose.id.core.persistence.entities.User;
import de.passau.uni.sec.compose.id.core.persistence.repository.ApplicationRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.AttributeDefinitionRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.AttributeValueRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.GroupRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.ServiceCompositionRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.ServiceInstanceRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.ServiceObjectRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.ServiceSourceCodeRepository;
import de.passau.uni.sec.compose.id.core.persistence.repository.UserRepository;
import de.passau.uni.sec.compose.id.core.service.security.RestAuthentication;
import de.passau.uni.sec.compose.id.core.service.security.uaa.OpenIdUserData;
import de.passau.uni.sec.compose.id.rest.messages.AttributeDefinitionResponseMessage;
import de.passau.uni.sec.compose.id.rest.messages.AttributeValueResponseMessage;
import de.passau.uni.sec.compose.id.rest.messages.EntityResponseMessage;
@Service
public class AttributeDefinitionService extends AbstractBasicListEntityService implements EntityService
{
private static Logger LOG = LoggerFactory.getLogger(AttributeDefinitionService.class);
@Autowired
GroupRepository groupRepository;
@Autowired
UserRepository userRepository;
@Autowired
RestAuthentication authentication;
@Autowired
AttributeDefinitionRepository attributeRepository;
@Override
protected IEntity getEntityById(String entityId) {
return attributeRepository.findOne(entityId);
}
@Override
public Object postACListAllEntities(Event event)
throws IdManagementException {
if(event instanceof ListAttributesInGroupEvent)
{
ListAttributesInGroupEvent gevent = (ListAttributesInGroupEvent)event;
Group g = groupRepository.findOne(gevent.getId() );
if(g == null)
throw new IdManagementException("Group not found",null, LOG," Attempt to list attribute definitions for non-existing group with id: "+gevent.getId()+", principals "+RestAuthentication.getBasicInfoPrincipals(event.getPrincipals()),Level.INFO, 404);
return getAttrubyteDefinitions(g);
}
throw new IdManagementException("Method not implemented",null, LOG,"The type of list functionality is not implemented",Level.ERROR, 501);
}
private Collection<AttributeDefinitionResponseMessage> getAttrubyteDefinitions(Group g) {
Collection<AttributeDefinitionResponseMessage> res = new LinkedList<>();
List<AttributeDefinition> defs = attributeRepository.findByGroup(g);
for(AttributeDefinition definition: defs)
{
res.add(new AttributeDefinitionResponseMessage(definition));
}
return res;
}
@Override
public void verifyACListAllEntities(Event event)
throws IdManagementException {
if(event instanceof ListAttributesInGroupEvent)
{
//Just check that there is only one principal
Collection<IPrincipal> principals = event.getPrincipals();
if(principals == null || principals.size()==0)
throw new IdManagementException("Authentication required.",null, LOG," Attempt to list unapproved memberships without providing credentials",Level.DEBUG, 401);
if(principals.size()!=1)
throw new IdManagementException("Only one user principal should call this API endpoint.",null, LOG,"There is more than one principal for getting pending attribute value approvals",Level.DEBUG, 401);
}
else
throw new IdManagementException("Method not implemented",null, LOG,"The type of list functionality is not implemented",Level.DEBUG, 501);
}
@Override
protected EntityResponseMessage postACCreateEntity(Event event)
throws IdManagementException {
CreateAttributeDefinitionEvent attrEvent= (CreateAttributeDefinitionEvent) event;
Group groupDefinition = groupRepository.findOne(attrEvent.getGroup_id());
if(groupDefinition == null)
throw new IdManagementException("Group not found",null, LOG," Attempt to list attribute definitions for non-existing group with id: "+attrEvent.getGroup_id()+", principals "+RestAuthentication.getBasicInfoPrincipals(event.getPrincipals()),Level.ERROR, 404);
List<AttributeDefinition> defs = attributeRepository.findByNameAndGroup(attrEvent.getMessage().getName(), groupDefinition);
if(defs.size()!=0)
throw new IdManagementException("Attribute definition already extists",null, LOG,"Attempt to create an attribute definition already exists in group with id: "+attrEvent.getGroup_id()+" with name: "+attrEvent.getMessage().getName()+", principals "+RestAuthentication.getBasicInfoPrincipals(attrEvent.getPrincipals()),Level.DEBUG, 409);
AttributeDefinition attributeDef = new AttributeDefinition();
//ownership or admin role in the the Group is checked in ACcess control method already
attributeDef.setGroup(groupDefinition);
attributeDef.setId(UUID.randomUUID().toString());
attributeDef.setName(attrEvent.getMessage().getName());
attributeDef.setType(attrEvent.getMessage().getType());
attributeRepository.save(attributeDef);
return new AttributeDefinitionResponseMessage(attributeDef);
}
@Override
protected EntityResponseMessage postACGetEntity(Event event)
throws IdManagementException {
//TODO
return null;
}
@Override
protected EntityResponseMessage postACUpdateEntity(DetailsIdEvent event,
IEntity previous) throws IdManagementException {
throw new IdManagementException("Method not implemented",null, LOG,"The type of list functionality is not implemented",Level.DEBUG, 501);
}
@Override
protected void verifyAccessControlCreateEntity(Event event)
throws IdManagementException {
CreateAttributeDefinitionEvent attrEvent= ((CreateAttributeDefinitionEvent)event);
Collection<IPrincipal> principals = event.getPrincipals();
for(IPrincipal p: principals)
{
if(p instanceof ComposeUserPrincipal)
{
ComposeUserPrincipal user = ((ComposeUserPrincipal)p);
OpenIdUserData userData = user.getOpenId();
User u = userRepository.getOne(userData.getUser_id());
Group g = groupRepository.findOne(attrEvent.getGroup_id());
if(g == null)
throw new IdManagementException("Group not found",null, LOG," Attempt to list attribute definitions for non-existing group with id: "+attrEvent.getGroup_id()+", principals "+RestAuthentication.getBasicInfoPrincipals(event.getPrincipals()),Level.ERROR, 404);
if(!u.isGroupAdmin(g.getId()) && !u.equals(g.getOwner()))
throw new IdManagementException("Not sufficient permissions for the action requred : Attempt to create attribute defintition in a group for which the user is no the owner nor the admin",
null, LOG,
"Attempt to create an attribute defintion in a group for which the user is no the owner nor the admin. Group id: "+attrEvent.getGroup_id()+", principals "+RestAuthentication.getBasicInfoPrincipals(principals),Level.ERROR, 403);
}
}
}
@Override
protected void verifyAccessControlUpdateEntity(DetailsIdEvent event)
throws IdManagementException {
Collection<IPrincipal> principals = event.getPrincipals();
if(principals == null || principals.size()==0)
throw new IdManagementException("Authentication required.",null, LOG," Attempt to list attribute values without providing credentials",Level.DEBUG, 401);
if(principals.size()!=1)
throw new IdManagementException("Only one user principal should call this API endpoint.",null, LOG,"There is more than one principal for getting pending user approvals ",Level.DEBUG, 401);
//The rest of checks need to be done with the update since it must be specifically noticed whether the update is being done by the user, or the group owner/admin... see postACUPdate method...
}
@Override
protected void verifyAccessControlGetEntity(Event event)
throws IdManagementException {
}
@Override
protected void postACDeleteEntity(DetailsIdEvent event)
throws IdManagementException {
AttributeDefinition attempted = attributeRepository.findOne(event.getEntityId());
attributeRepository.delete(attempted);
}
@Override
protected void verifyAccessControlDeleteEntity(DetailsIdEvent event)
throws IdManagementException {
Collection<IPrincipal> principals = event.getPrincipals();
if(principals == null || principals.size()==0)
throw new IdManagementException("Authentication required.",null, LOG," Attempt to list unapproved memberships without providing credentials",Level.DEBUG, 401);
if(principals.size()!=1)
throw new IdManagementException("Only one user principal should call this API endpoint.",null, LOG,"There is more than one principal while trying to remove a user approval",Level.DEBUG, 401);
AttributeDefinition memb = attributeRepository.findOne(event.getEntityId());
IPrincipal prin = principals.iterator().next();
if(prin instanceof ComposeUserPrincipal)
{
Group g = memb.getGroup();
User u = userRepository.findOne(((ComposeUserPrincipal) prin).getOpenId().getUser_id());
if(!u.isGroupAdmin(g.getId()) && !u.equals(g.getOwner()))
throw new IdManagementException("Not sufficient permissions for the action requred : Attempt to delete attribute defintition for a group for which the user is no the owner nor the admin",
null, LOG,
"Attempt to delete an attribute defintion with id: "+event.getEntityId()+" in a group for which the user is no the owner nor the admin. Group id: "+g.getId()+", principals "+RestAuthentication.getBasicInfoPrincipals(principals),Level.ERROR, 403);
}
else
throw new IdManagementException("To delete an attribute, the principal must be a user",null, LOG,"Principal attempting to approve a membership is not a user. princpals: "+RestAuthentication.getBasicInfoPrincipals(event.getPrincipals()),Level.INFO, 401);
}
@Override
protected Logger getLogger()
{
return LOG;
}
}