Skip to content
Snippets Groups Projects
Verified Commit 3f58086d authored by Timm Fitschen's avatar Timm Fitschen
Browse files

WIP: fsm v0.2

parent b6fb9a7a
No related branches found
No related tags found
3 merge requests!21Release v0.4.0,!7F fsm,!6Draft: F acm permissions2
package org.caosdb.server.jobs.core;
import org.apache.shiro.authz.AuthorizationException;
import org.caosdb.server.entity.DeleteEntity;
import org.caosdb.server.entity.Message;
import org.caosdb.server.entity.Message.MessageType;
......@@ -7,6 +8,7 @@ import org.caosdb.server.entity.UpdateEntity;
import org.caosdb.server.jobs.JobAnnotation;
import org.caosdb.server.jobs.JobExecutionTime;
import org.caosdb.server.transaction.WriteTransaction;
import org.caosdb.server.utils.ServerMessages;
/**
* Check if the attempted state transition is allowed.
......@@ -46,6 +48,9 @@ public class CheckStateTransition extends EntityStateJob {
}
} catch (Message m) {
getEntity().addError(m);
} catch (AuthorizationException e) {
getEntity().addError(ServerMessages.AUTHORIZATION_ERROR);
getEntity().addInfo(e.getMessage());
}
}
......@@ -88,12 +93,20 @@ public class CheckStateTransition extends EntityStateJob {
return;
}
boolean transition_defined = false;
for (Transition t : stateModel.getTransitions()) {
if (t.isFromState(oldState) && t.isToState(newState)) {
// TODO permissions
transition_defined = true;
if (t.isPermitted(getUser())) {
return;
}
}
}
if (transition_defined) {
throw new AuthorizationException(
getUser().getPrincipal().toString()
+ " doesn't have permission to perform this transition.");
}
throw TRANSITION_NOT_ALLOWED;
}
......
package org.caosdb.server.jobs.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.apache.shiro.subject.Subject;
import org.caosdb.server.database.exceptions.EntityDoesNotExistException;
import org.caosdb.server.datatype.AbstractCollectionDatatype;
import org.caosdb.server.datatype.CollectionValue;
......@@ -21,6 +25,8 @@ import org.caosdb.server.entity.StatementStatus;
import org.caosdb.server.entity.wrapper.Property;
import org.caosdb.server.entity.xml.ToElementable;
import org.caosdb.server.jobs.EntityJob;
import org.caosdb.server.permissions.EntityACI;
import org.caosdb.server.permissions.EntityACL;
import org.caosdb.server.query.Query;
import org.caosdb.server.utils.EntityStatus;
import org.jdom2.Element;
......@@ -58,9 +64,16 @@ public abstract class EntityStateJob extends EntityJob {
public static final String STATE_RECORD_TYPE_NAME = "State";
public static final String STATE_MODEL_RECORD_TYPE_NAME = "StateModel";
public static final String TRANSITION_RECORD_TYPE_NAME = "Transition";
public static final String TRANSITION_XML_TAG = "Transition";
public static final String TRANSITION_ATTRIBUTE_NAME = "name";
public static final String TRANSITION_ATTRIBUTE_DESCRIPTION = "description";
public static final String TO_XML_TAG = "ToState";
public static final String FROM_XML_TAG = "FromState";
public static final String STATE_XML_TAG = "State";
public static final String MODEL_ATTRIBUTE_NAME = "model";
public static final String STATE_ATTRIBUTE_MODEL = "model";
public static final String STATE_ATTRIBUTE_NAME = "name";
public static final String STATE_ATTRIBUTE_DESCRIPTION = "description";
public static final String STATE_ATTRIBUTE_ID = "id";
public static final Message STATE_MODEL_NOT_FOUND =
new Message(MessageType.Error, "StateModel not found.");
......@@ -86,12 +99,14 @@ public abstract class EntityStateJob extends EntityJob {
*/
public class Transition {
private String name;
private String description;
private State fromState;
private State toState;
private String name;
public Transition(EntityInterface transition) throws Message {
this.name = transition.getName();
this.description = transition.getDescription();
this.fromState = getFromState(transition);
this.toState = getToState(transition);
}
......@@ -153,6 +168,10 @@ public abstract class EntityStateJob extends EntityJob {
return this.name;
}
public String getDescription() {
return this.description;
}
@Override
public String toString() {
return "Transition (name="
......@@ -163,6 +182,22 @@ public abstract class EntityStateJob extends EntityJob {
+ getToState().getStateName()
+ ")";
}
public Element toElement() {
Element result = new Element(TRANSITION_XML_TAG);
if (this.name != null) result.setAttribute(TRANSITION_ATTRIBUTE_NAME, this.name);
if (this.description != null)
result.setAttribute(TRANSITION_ATTRIBUTE_DESCRIPTION, this.description);
Element to = new Element(TO_XML_TAG);
to.setAttribute(STATE_ATTRIBUTE_NAME, this.toState.stateName);
Element from = new Element(FROM_XML_TAG);
from.setAttribute(STATE_ATTRIBUTE_NAME, this.fromState.stateName);
return result.addContent(from).addContent(to);
}
public boolean isPermitted(Subject user) {
return user.isPermitted("STATE:TRANSITION:" + this.name);
}
}
/**
......@@ -191,6 +226,9 @@ public abstract class EntityStateJob extends EntityJob {
private EntityInterface stateEntity = null;
private EntityInterface stateModelEntity = null;
private StateModel stateModel;
private String stateDescription = null;
private Integer stateId = null;
private EntityACL stateACL = null;
public State(String stateName, String stateModelName) throws Message {
this.stateName = stateName;
......@@ -199,9 +237,37 @@ public abstract class EntityStateJob extends EntityJob {
public State(EntityInterface stateEntity, EntityInterface stateModelEntity) throws Message {
this.stateEntity = stateEntity;
this.stateDescription = stateEntity.getDescription();
this.stateId = stateEntity.getId();
this.stateName = stateEntity.getName();
this.stateModelEntity = stateModelEntity;
this.stateModelName = stateModelEntity.getName();
this.stateACL = createStateACL(stateEntity.getEntityACL());
}
private EntityACL createStateACL(EntityACL entityACL) {
LinkedList<EntityACI> rules = new LinkedList<>();
for (EntityACI aci : entityACL.getRules()) {
if (aci.getResponsibleAgent().toString().startsWith("?STATE?")) {
int end = aci.getResponsibleAgent().toString().length() - 1;
String role = aci.getResponsibleAgent().toString().substring(7, end);
rules.add(
new EntityACI(org.caosdb.server.permissions.Role.create(role), aci.getBitSet()));
}
}
return new EntityACL(rules);
}
public EntityACL getStateACL() {
return this.stateACL;
}
public String getStateDescription() throws Message {
return this.stateDescription;
}
public Integer getStateId() throws Message {
return this.stateId;
}
@Override
......@@ -223,11 +289,31 @@ public abstract class EntityStateJob extends EntityJob {
@Override
public void addToElement(Element ret) {
Element e = new Element(STATE_XML_TAG);
if (this.getStateModelName() != null) {
e.setAttribute(MODEL_ATTRIBUTE_NAME, this.getStateModelName());
if (this.stateModelName != null) {
e.setAttribute(STATE_ATTRIBUTE_MODEL, this.stateModelName);
}
if (this.stateName != null) {
e.setAttribute(STATE_ATTRIBUTE_NAME, this.stateName);
}
if (this.stateDescription != null) {
e.setAttribute(STATE_ATTRIBUTE_DESCRIPTION, this.stateDescription);
}
if (this.stateId != null) {
e.setAttribute(STATE_ATTRIBUTE_ID, Integer.toString(this.stateId));
}
if (this.stateModelEntity != null) {
try {
this.getStateModel()
.transitions
.forEach(
(Transition t) -> {
if (t.isFromState(this) && t.isPermitted(getUser())) {
e.addContent(t.toElement());
}
});
} catch (Message m) {
getEntity().addError(m);
}
if (this.getStateName() != null) {
e.setAttribute(STATE_ATTRIBUTE_NAME, this.getStateName());
}
ret.addContent(e);
}
......@@ -281,6 +367,9 @@ public abstract class EntityStateJob extends EntityJob {
public EntityInterface getStateEntity() throws Message {
if (this.stateEntity == null) {
this.stateEntity = retrieveStateEntity(this.getStateName());
this.stateDescription = this.stateEntity.getDescription();
this.stateId = this.stateEntity.getId();
this.stateACL = createStateACL(this.stateEntity.getEntityACL());
}
return this.stateEntity;
}
......@@ -321,16 +410,11 @@ public abstract class EntityStateJob extends EntityJob {
private State finalState;
public StateModel(EntityInterface stateModelEntity) throws Message {
long time1 = System.currentTimeMillis();
this.name = stateModelEntity.getName();
this.transitions = getTransitions(stateModelEntity);
this.states = getStates(transitions, this);
this.finalState = getFinalState(stateModelEntity);
this.initialState = getInitialState(stateModelEntity);
long time2 = System.currentTimeMillis();
getTransaction()
.getTransactionBenchmark()
.addMeasurement(this.getClass().getSimpleName() + "::1", time2 - time1);
}
private State getInitialState(EntityInterface stateModelEntity) throws Message {
......@@ -546,7 +630,7 @@ public abstract class EntityStateJob extends EntityJob {
}
protected State createState(ClientMessage s) throws Message {
String stateModel = s.getProperty(MODEL_ATTRIBUTE_NAME);
String stateModel = s.getProperty(STATE_ATTRIBUTE_MODEL);
if (stateModel == null) {
throw STATE_MODEL_NOT_SPECIFIED;
}
......@@ -570,7 +654,12 @@ public abstract class EntityStateJob extends EntityJob {
try {
p.parseValue();
ReferenceValue refid = (ReferenceValue) p.getValue();
EntityInterface stateEntity = retrieveValidSparseEntityById(refid.getId(), null);
EntityInterface stateEntity = cache.get("state" + Integer.toString(refid.getId()));
boolean cached = true;
if (stateEntity == null || !cached) {
stateEntity = retrieveValidSparseEntityById(refid.getId(), null);
cache.put("state" + Integer.toString(refid.getId()), stateEntity);
}
EntityInterface stateModelEntity = findStateModel(stateEntity);
return new State(stateEntity, stateModelEntity);
......@@ -581,8 +670,16 @@ public abstract class EntityStateJob extends EntityJob {
}
}
private static final Map<String, EntityInterface> cache = new HashMap<>();
EntityInterface findStateModel(EntityInterface stateEntity) throws Exception {
// TODO this should be cached
boolean cached = true;
EntityInterface result = cache.get("modelof" + Integer.toString(stateEntity.getId()));
if (result != null && cached) {
return result;
}
Query query =
new Query(
"FIND RECORD "
......@@ -594,6 +691,8 @@ public abstract class EntityStateJob extends EntityJob {
getUser(),
null);
query.execute(getTransaction().getAccess());
return retrieveValidEntity(query.getResultSet().get(0));
result = retrieveValidEntity(query.getResultSet().get(0));
cache.put("modelof" + Integer.toString(stateEntity.getId()), result);
return result;
}
}
......@@ -67,6 +67,7 @@ public class InitEntityStateJobs extends EntityStateJob implements Observer {
oldState = null;
if (states.size() == 1) {
oldState = states.get(0);
((UpdateEntity) getEntity()).getOriginal().setEntityACL(getEntity().getEntityACL());
}
if (!Objects.equals(newState, oldState)) {
getEntity().acceptObserver(this);
......@@ -92,13 +93,20 @@ public class InitEntityStateJobs extends EntityStateJob implements Observer {
MessageType.Error, "Currently, each entity can only have one state at a time.");
} else if (states.size() == 1) {
newState = states.get(0);
transferEntityACL(getEntity(), newState);
}
} catch (Message m) {
getEntity().addError(m);
}
return newState;
}
private void transferEntityACL(EntityInterface entity, State newState) throws Message {
newState.getStateEntity();
entity.setEntityACL(newState.getStateACL());
}
private static final Message STATE_ERROR_IN_ORIGINAL_ENTITY(Message m) {
return new Message(
MessageType.Warning, "State error in previous entity version\n" + m.getDescription());
......
......@@ -4,9 +4,11 @@ import java.util.List;
import org.caosdb.server.CaosDBServer;
import org.caosdb.server.entity.Message;
import org.caosdb.server.entity.wrapper.Property;
import org.caosdb.server.entity.xml.ToElementable;
import org.caosdb.server.jobs.JobAnnotation;
import org.caosdb.server.jobs.JobExecutionTime;
import org.caosdb.server.transaction.Retrieve;
import org.jdom2.Element;
/**
* Remove the state property from the entity and, iff necessary, convert it into a State instance
......@@ -23,6 +25,8 @@ import org.caosdb.server.transaction.Retrieve;
time = JobExecutionTime.POST_TRANSACTION)
public class MakeStateMessage extends EntityStateJob {
public static final String SPARSE_FLAG = "sparseState";
@Override
protected void run() {
......@@ -33,11 +37,29 @@ public class MakeStateMessage extends EntityStateJob {
// only add the State during Retrieve. In all other cases, the State is already present.
if (stateProperties != null && getTransaction() instanceof Retrieve) {
String sparse =
getTransaction().getContainer().getFlags().getOrDefault(SPARSE_FLAG, "false");
if ("true".equals(sparse)) {
for (Property s : stateProperties) {
getEntity()
.addMessage(
new ToElementable() {
@Override
public void addToElement(Element ret) {
Element state = new Element(STATE_XML_TAG);
state.setAttribute(STATE_ATTRIBUTE_ID, s.getValue().toString());
ret.addContent(state);
}
});
}
} else {
for (Property s : stateProperties) {
State stateMessage = createState(s);
stateMessage.getStateModel();
getEntity().addMessage(stateMessage);
}
}
}
} catch (Message e) {
getEntity().addError(e);
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment