Skip to content
Snippets Groups Projects
Unverified Commit 95a7e24b authored by Timm Fitschen's avatar Timm Fitschen
Browse files

ENH: AWI Box Loan Feature

parent 19124d74
Branches
Tags
No related merge requests found
package caosdb.server.jobs.extension;
import caosdb.server.CaosDBServer;
import caosdb.server.accessControl.UserSources;
import caosdb.server.entity.Entity;
import caosdb.server.entity.EntityInterface;
import caosdb.server.entity.Message;
import caosdb.server.entity.wrapper.Property;
import caosdb.server.jobs.ContainerJob;
import caosdb.server.jobs.JobAnnotation;
import caosdb.server.jobs.core.CheckNoAdditionalPropertiesPresent;
import caosdb.server.jobs.core.CheckNoOverridesPresent;
import caosdb.server.jobs.core.CheckPropValid;
import caosdb.server.query.Query;
import caosdb.server.transaction.Insert;
import caosdb.server.transaction.Update;
import caosdb.server.utils.EntityStatus;
import caosdb.server.utils.ServerMessages;
import caosdb.server.utils.Utils;
import java.util.List;
@JobAnnotation(transaction = caosdb.server.transaction.WriteTransaction.class, loadAlways = true)
public class AWIBoxLoan extends ContainerJob {
private static final Message UNIQUE_USER =
new Message("The user must have a unique combination of first name and last name!");
private static final Message BOX_HAS_LOAN =
new Message(
"This box cannot be be requested right now because it appears to have a Loan property attached to it. This usually means, that the box is already requested or borrowed by someone.");
@Override
protected void run() {
if (isAnonymous()
&& !(isRequestLoanSetUser()
|| isRequestLoanInsertLoan()
|| isRequestLoanUpdateBox()
|| isRequestReturnSetUser()
|| isRequestReturnUpdateLoan())) {
addError(ServerMessages.AUTHORIZATION_ERROR);
}
}
boolean isAnonymous() {
return getUser().hasRole(UserSources.ANONYMOUS_ROLE);
}
boolean isRequestReturnUpdateLoan() {
// is UPDATE transaction
if (getTransaction() instanceof Update) {
// Container has only loan elements with special properties
for (EntityInterface e : getContainer()) {
if (!isLoan(e) || !hasOnlyAllowedLoanProperties4RequestReturn(e)) {
return false;
}
setReturnRequestedDate(e);
}
return true;
}
return false;
}
boolean isRequestReturnSetUser() {
// same as request_loan.set_user
return isRequestLoanSetUser();
}
boolean isRequestLoanUpdateBox() {
// is UPDATE transaction
if (getTransaction() instanceof Update) {
// Container has only box elements
for (EntityInterface e : getContainer()) {
if (boxHasLoanProperty(e)) {
e.addError(BOX_HAS_LOAN);
return true;
}
if (!isBox(e) || !hasOnlyAllowedBoxProperties4RequestLoan(e)) {
return false;
}
}
return true;
}
return false;
}
private boolean boxHasLoanProperty(EntityInterface e) {
EntityInterface validBox = retrieveValidEntity(e.getId());
for (Property p : validBox.getProperties()) {
if (p.getId() == getLoanId()) {
return true;
}
}
return false;
}
/**
* Has only one new property -> Loan.
*
* @param e
* @return
*/
boolean hasOnlyAllowedBoxProperties4RequestLoan(EntityInterface e) {
int count = 0;
for (Property p : e.getProperties()) {
if (p.getEntityStatus() == EntityStatus.QUALIFIED) { // this means update
if (p.getId() == getLoanId()) {
count++;
continue;
}
return false; // this is not a Loan.
}
}
// Box has only one update, a loan property
return count == 1;
}
boolean isRequestLoanInsertLoan() {
// is INSERT transaction
if (getTransaction() instanceof Insert) {
// Container has only loan elements
for (EntityInterface e : getContainer()) {
if (!isLoan(e)) {
return false;
}
setLoanRequestDate(e);
// Loans have only a specific set of properties
appendJob(e, CheckNoAdditionalPropertiesPresent.class);
appendJob(e, CheckNoOverridesPresent.class);
}
return true;
}
return false;
}
void setReturnRequestedDate(EntityInterface e) {
setDateProperty(e, getReturnRequestedId());
}
private void setDateProperty(EntityInterface e, Integer propertyId) {
EntityInterface p = retrieveValidEntity(propertyId);
p.setValue(getTransaction().getTimestamp());
e.addProperty(new Property(p));
}
void setLoanRequestDate(EntityInterface e) {
setDateProperty(e, getLoanRequestedId());
}
boolean isRequestLoanSetUser() {
// is INSERT/UPDATE transaction
// Container has only one element, user
if ((getTransaction() instanceof Update || getTransaction() instanceof Insert)
&& getContainer().size() == 1
&& isUser(getContainer().get(0))
&& checkUniqueName(getContainer().get(0))
&& checkEmail(getContainer().get(0))) {
appendJob(getContainer().get(0), CheckNoAdditionalPropertiesPresent.class);
appendJob(getContainer().get(0), CheckNoOverridesPresent.class);
return true;
}
return false;
}
boolean checkEmail(Entity entity) {
runJobFromSchedule(entity, CheckPropValid.class);
for (Property p : entity.getProperties()) {
if (p.getId() == getEmailID()) {
if (!Utils.isRFC822Compliant(p.getValue().toString())) {
p.addError(ServerMessages.EMAIL_NOT_WELL_FORMED);
}
return true;
}
}
return false;
}
private boolean checkUniqueName(Entity entity) {
String firstName = null;
String lastName = null;
Query q =
new Query(
"FIND "
+ getUserID().toString()
+ " WITH "
+ getFirstNameId().toString()
+ "='"
+ firstName
+ "' AND "
+ getLastNameId().toString()
+ "='"
+ lastName
+ "'",
getUser())
.execute(getTransaction().getAccess());
List<Integer> resultSet = q.getResultSet();
if (resultSet.isEmpty() || (resultSet.size() == 1 && resultSet.get(0) == entity.getId())) {
return true;
}
entity.addError(UNIQUE_USER);
return false;
}
/** Has single user parent. */
boolean isUser(Entity entity) {
return entity.getParents().size() == 1
&& retrieveValidIDByName(entity.getParents().get(0).getName()) == getUserID();
}
/** Has single box parent. */
boolean isBox(EntityInterface e) {
return e.getParents().size() == 1
&& retrieveValidIDByName(e.getParents().get(0).getName()) == getBoxId();
}
/** has single loan parent */
private boolean isLoan(EntityInterface e) {
return e.getParents().size() == 1
&& retrieveValidIDByName(e.getParents().get(0).getName()) == getLoanId();
}
/**
* Has only 5/6 new/updated properties: content, returnRequested, destination, Borrower, comment
* (optional), location
*/
boolean hasOnlyAllowedLoanProperties4RequestReturn(EntityInterface e) {
runJobFromSchedule(e, CheckPropValid.class);
for (Property p : e.getProperties()) {
if (p.getEntityStatus() == EntityStatus.QUALIFIED) { // this means update
if (p.getId() == getContentId()) {
} else if (p.getId() == getDestinationId()) {
} else if (p.getId() == getBorrowerId()) {
} else if (p.getId() == getCommentId()) {
} else if (p.getId() == getLocationId()) {
}
return false; // this is not a property which may be updated by anonymous.
}
}
return true;
}
Integer getIdOf(String string) {
String id = CaosDBServer.getServerProperty("EXT_AWI_" + string.toUpperCase() + "_ID");
if (id != null && Utils.isNonNullInteger(id)) {
return new Integer(id);
}
String name = CaosDBServer.getServerProperty("EXT_AWI_" + string.toUpperCase() + "_NAME");
if (name == null || name.isEmpty()) {
name = string;
}
return retrieveValidIDByName(name);
}
Integer getBorrowerId() {
return getIdOf("Borrower");
}
Integer getCommentId() {
return getIdOf("comment");
}
Integer getLocationId() {
return getIdOf("location");
}
Integer getDestinationId() {
return getIdOf("destination");
}
Integer getContentId() {
return getIdOf("content");
}
Integer getBoxId() {
return getIdOf("Box");
}
Integer getLoanId() {
return getIdOf("Loan");
}
Integer getUserID() {
return getIdOf("User");
}
Integer getEmailID() {
return getIdOf("email");
}
Integer getLoanRequestedId() {
return getIdOf("loanRequested");
}
Integer getReturnRequestedId() {
return getIdOf("returnRequested");
}
Integer getLastNameId() {
return getIdOf("lastName");
}
Integer getFirstNameId() {
return getIdOf("firstName");
}
}
package caosdb.server.jobs.extension;
import static org.junit.Assert.assertEquals;
import caosdb.server.entity.container.TransactionContainer;
import caosdb.server.jobs.core.Mode;
import caosdb.server.transaction.Transaction;
import caosdb.server.utils.EntityStatus;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.Permission;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.ExecutionException;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
public class TestAWIBoxLoan {
@Test
public void testNonAnonymousUser() {
TransactionContainer container = new TransactionContainer();
AWIBoxLoan j =
new AWIBoxLoan() {
@Override
protected Subject getUser() {
return new Subject() {
@Override
public void runAs(PrincipalCollection principals)
throws NullPointerException, IllegalStateException {}
@Override
public PrincipalCollection releaseRunAs() {
return null;
}
@Override
public void logout() {}
@Override
public void login(AuthenticationToken token) throws AuthenticationException {}
@Override
public boolean isRunAs() {
return false;
}
@Override
public boolean isRemembered() {
return false;
}
@Override
public boolean isPermittedAll(Collection<Permission> permissions) {
return false;
}
@Override
public boolean isPermittedAll(String... permissions) {
return false;
}
@Override
public boolean[] isPermitted(List<Permission> permissions) {
return null;
}
@Override
public boolean[] isPermitted(String... permissions) {
return null;
}
@Override
public boolean isPermitted(Permission permission) {
return false;
}
@Override
public boolean isPermitted(String permission) {
return false;
}
@Override
public boolean isAuthenticated() {
return false;
}
@Override
public boolean[] hasRoles(List<String> roleIdentifiers) {
return null;
}
@Override
public boolean hasRole(String roleIdentifier) {
return false;
}
@Override
public boolean hasAllRoles(Collection<String> roleIdentifiers) {
return false;
}
@Override
public Session getSession(boolean create) {
return null;
}
@Override
public Session getSession() {
return null;
}
@Override
public PrincipalCollection getPrincipals() {
return null;
}
@Override
public Object getPrincipal() {
return null;
}
@Override
public PrincipalCollection getPreviousPrincipals() {
return null;
}
@Override
public void execute(Runnable runnable) {}
@Override
public <V> V execute(Callable<V> callable) throws ExecutionException {
return null;
}
@Override
public void checkRoles(String... roleIdentifiers) throws AuthorizationException {}
@Override
public void checkRoles(Collection<String> roleIdentifiers)
throws AuthorizationException {}
@Override
public void checkRole(String roleIdentifier) throws AuthorizationException {}
@Override
public void checkPermissions(Collection<Permission> permissions)
throws AuthorizationException {}
@Override
public void checkPermissions(String... permissions) throws AuthorizationException {}
@Override
public void checkPermission(Permission permission) throws AuthorizationException {}
@Override
public void checkPermission(String permission) throws AuthorizationException {}
@Override
public Runnable associateWith(Runnable runnable) {
return null;
}
@Override
public <V> Callable<V> associateWith(Callable<V> callable) {
return null;
}
};
}
};
Transaction<TransactionContainer> t =
new Transaction<TransactionContainer>(container, null) {
@Override
protected void transaction() throws Exception {}
@Override
protected void preTransaction() throws InterruptedException {}
@Override
protected void preCheck() throws InterruptedException, Exception {}
@Override
protected void postTransaction() throws Exception {}
@Override
protected void postCheck() {}
@Override
public boolean logHistory() {
return false;
}
@Override
protected void init() throws Exception {}
@Override
protected void cleanUp() {}
};
j.init(Mode.MUST, null, t);
assertEquals(0, j.getContainer().getMessages().size());
assertEquals(EntityStatus.QUALIFIED, j.getContainer().getStatus());
// non-anonymous user
j.run();
assertEquals(0, j.getContainer().getMessages().size());
assertEquals(EntityStatus.QUALIFIED, j.getContainer().getStatus());
}
@Test
public void testAnonymousUserUnqualified() {
TransactionContainer container = new TransactionContainer();
AWIBoxLoan j =
new AWIBoxLoan() {
@Override
protected Subject getUser() {
return null;
}
@Override
boolean isAnonymous() {
return true;
}
};
Transaction<TransactionContainer> t =
new Transaction<TransactionContainer>(container, null) {
@Override
protected void transaction() throws Exception {}
@Override
protected void preTransaction() throws InterruptedException {}
@Override
protected void preCheck() throws InterruptedException, Exception {}
@Override
protected void postTransaction() throws Exception {}
@Override
protected void postCheck() {}
@Override
public boolean logHistory() {
return false;
}
@Override
protected void init() throws Exception {}
@Override
protected void cleanUp() {}
};
j.init(Mode.MUST, null, t);
assertEquals(0, j.getContainer().getMessages().size());
assertEquals(EntityStatus.QUALIFIED, j.getContainer().getStatus());
j.run();
assertEquals(1, j.getContainer().getMessages().size());
assertEquals(EntityStatus.UNQUALIFIED, j.getContainer().getStatus());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment