Commit 682bbf71 authored by michael.simon's avatar michael.simon
Browse files

Merge branch 'create_scripted_workflows' into 'branch-2.5'

Create scripted workflows

See merge request simon/reg-app!3
parents 8f865587 1dd80194
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.dao;
import edu.kit.scc.webreg.entity.ScriptEntity;
public interface ScriptDao extends BaseDao<ScriptEntity, Long> {
ScriptEntity findByName(String name);
}
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.dao.jpa;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import javax.persistence.NoResultException;
import edu.kit.scc.webreg.dao.ScriptDao;
import edu.kit.scc.webreg.entity.ScriptEntity;
@Named
@ApplicationScoped
public class JpaScriptlDao extends JpaBaseDao<ScriptEntity, Long> implements ScriptDao {
@Override
public ScriptEntity findByName(String name) {
try {
return (ScriptEntity) em.createQuery("select e from ScriptEntity e where e.name = :name")
.setParameter("name", name).getSingleResult();
} catch (NoResultException e) {
return null;
}
}
@Override
public Class<ScriptEntity> getEntityClass() {
return ScriptEntity.class;
}
}
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.entity;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Lob;
import javax.persistence.Table;
import org.hibernate.annotations.Type;
@Entity(name = "ScriptEntity")
@Table(name = "script")
public class ScriptEntity extends AbstractBaseEntity {
private static final long serialVersionUID = 1L;
@Column(name = "script_text")
@Lob
@Type(type = "org.hibernate.type.TextType")
private String script;
@Column(name = "script_type", length = 32)
private String scriptType;
@Column(name = "script_engine", length = 64)
private String scriptEngine;
@Column(name = "name", length = 128)
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getScript() {
return script;
}
public void setScript(String script) {
this.script = script;
}
public String getScriptType() {
return scriptType;
}
public void setScriptType(String scriptType) {
this.scriptType = scriptType;
}
public String getScriptEngine() {
return scriptEngine;
}
public void setScriptEngine(String scriptEngine) {
this.scriptEngine = scriptEngine;
}
}
package edu.kit.scc.webreg.entity;
import javax.annotation.Generated;
import javax.persistence.metamodel.SingularAttribute;
import javax.persistence.metamodel.StaticMetamodel;
@Generated(value = "org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor")
@StaticMetamodel(ScriptEntity.class)
public abstract class ScriptEntity_ extends edu.kit.scc.webreg.entity.AbstractBaseEntity_ {
public static volatile SingularAttribute<ScriptEntity, String> script;
public static volatile SingularAttribute<ScriptEntity, String> scriptType;
}
package edu.kit.scc.webreg.script;
import java.io.Serializable;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.inject.Named;
import edu.kit.scc.webreg.dao.GroupDao;
import edu.kit.scc.webreg.dao.RegistryDao;
import edu.kit.scc.webreg.dao.ScriptDao;
import edu.kit.scc.webreg.dao.ServiceDao;
import edu.kit.scc.webreg.dao.UserDao;
@Named
@ApplicationScoped
public class ScriptingEnv implements Serializable {
private static final long serialVersionUID = 1L;
@Inject
private ScriptDao scriptDao;
@Inject
private UserDao userDao;
@Inject
private GroupDao groupDao;
@Inject
private ServiceDao serviceDao;
@Inject
private RegistryDao registryDao;
public UserDao getUserDao() {
return userDao;
}
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public GroupDao getGroupDao() {
return groupDao;
}
public void setGroupDao(GroupDao groupDao) {
this.groupDao = groupDao;
}
public ServiceDao getServiceDao() {
return serviceDao;
}
public void setServiceDao(ServiceDao serviceDao) {
this.serviceDao = serviceDao;
}
public RegistryDao getRegistryDao() {
return registryDao;
}
public void setRegistryDao(RegistryDao registryDao) {
this.registryDao = registryDao;
}
public ScriptDao getScriptDao() {
return scriptDao;
}
public void setScriptDao(ScriptDao scriptDao) {
this.scriptDao = scriptDao;
}
}
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.service.reg;
import edu.kit.scc.webreg.script.ScriptingEnv;
public interface ScriptingWorkflow {
void setScriptingEnv(ScriptingEnv env);
}
......@@ -47,9 +47,11 @@ import edu.kit.scc.webreg.event.MultipleGroupEvent;
import edu.kit.scc.webreg.event.ServiceRegisterEvent;
import edu.kit.scc.webreg.exc.EventSubmitException;
import edu.kit.scc.webreg.exc.RegisterException;
import edu.kit.scc.webreg.script.ScriptingEnv;
import edu.kit.scc.webreg.service.reg.ApprovalService;
import edu.kit.scc.webreg.service.reg.ApprovalWorkflow;
import edu.kit.scc.webreg.service.reg.RegisterUserWorkflow;
import edu.kit.scc.webreg.service.reg.ScriptingWorkflow;
@Stateless
public class ApprovalServiceImpl implements ApprovalService {
......@@ -84,6 +86,9 @@ public class ApprovalServiceImpl implements ApprovalService {
@Inject
private ApplicationConfig appConfig;
@Inject
private ScriptingEnv scriptingEnv;
@Override
public void registerApproval(RegistryEntity registry, Auditor parentAuditor) throws RegisterException {
ApprovalWorkflow workflow = getApprovalWorkflowInstance(registry.getApprovalBean());
......@@ -140,7 +145,7 @@ public class ApprovalServiceImpl implements ApprovalService {
registry.getUser().getEppn(), registry.getService().getName());
RegisterUserWorkflow workflow = getRegisterWorkflowInstance(registry.getRegisterBean());
try {
ServiceEntity serviceEntity = serviceDao.findByIdWithServiceProps(registry.getService().getId());
......@@ -205,8 +210,12 @@ public class ApprovalServiceImpl implements ApprovalService {
private RegisterUserWorkflow getRegisterWorkflowInstance(String className) {
try {
Object o = Class.forName(className).newInstance();
if (o instanceof RegisterUserWorkflow)
if (o instanceof RegisterUserWorkflow) {
if (o instanceof ScriptingWorkflow)
((ScriptingWorkflow) o).setScriptingEnv(scriptingEnv);
return (RegisterUserWorkflow) o;
}
else {
logger.warn("Service Register bean misconfigured, Object not Type RegisterUserWorkflow but: {}", o.getClass());
return null;
......
......@@ -64,12 +64,14 @@ import edu.kit.scc.webreg.event.MultipleGroupEvent;
import edu.kit.scc.webreg.event.ServiceRegisterEvent;
import edu.kit.scc.webreg.exc.EventSubmitException;
import edu.kit.scc.webreg.exc.RegisterException;
import edu.kit.scc.webreg.script.ScriptingEnv;
import edu.kit.scc.webreg.service.reg.ApprovalService;
import edu.kit.scc.webreg.service.reg.GroupCapable;
import edu.kit.scc.webreg.service.reg.GroupUtil;
import edu.kit.scc.webreg.service.reg.PasswordUtil;
import edu.kit.scc.webreg.service.reg.RegisterUserService;
import edu.kit.scc.webreg.service.reg.RegisterUserWorkflow;
import edu.kit.scc.webreg.service.reg.ScriptingWorkflow;
import edu.kit.scc.webreg.service.reg.SetPasswordCapable;
@Stateless
......@@ -120,6 +122,9 @@ public class RegisterUserServiceImpl implements RegisterUserService {
@Inject
private ApplicationConfig appConfig;
@Inject
private ScriptingEnv scriptingEnv;
@Override
public void registerUser(UserEntity user, ServiceEntity service, String executor)
throws RegisterException {
......@@ -343,6 +348,7 @@ public class RegisterUserServiceImpl implements RegisterUserService {
group = groupDao.findWithUsers(group.getId());
RegisterUserWorkflow workflow = getWorkflowInstance(service.getRegisterBean());
if (! (workflow instanceof GroupCapable)) {
logger.warn("Workflow " + workflow.getClass() + " is not GroupCapable! But Group will be deleted anyway.");
return;
......@@ -597,8 +603,12 @@ public class RegisterUserServiceImpl implements RegisterUserService {
public RegisterUserWorkflow getWorkflowInstance(String className) {
try {
Object o = Class.forName(className).newInstance();
if (o instanceof RegisterUserWorkflow)
if (o instanceof RegisterUserWorkflow) {
if (o instanceof ScriptingWorkflow)
((ScriptingWorkflow) o).setScriptingEnv(scriptingEnv);
return (RegisterUserWorkflow) o;
}
else {
logger.warn("Service Register bean misconfigured, Object not Type RegisterUserWorkflow but: {}", o.getClass());
return null;
......
/*******************************************************************************
* Copyright (c) 2014 Michael Simon.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
* Michael Simon - initial
******************************************************************************/
package edu.kit.scc.webreg.service.reg.ldap;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import edu.kit.scc.webreg.audit.Auditor;
import edu.kit.scc.webreg.entity.GroupEntity;
import edu.kit.scc.webreg.entity.RegistryEntity;
import edu.kit.scc.webreg.entity.ScriptEntity;
import edu.kit.scc.webreg.entity.ServiceEntity;
import edu.kit.scc.webreg.entity.UserEntity;
import edu.kit.scc.webreg.entity.audit.AuditStatus;
import edu.kit.scc.webreg.exc.PropertyReaderException;
import edu.kit.scc.webreg.exc.RegisterException;
import edu.kit.scc.webreg.script.ScriptingEnv;
import edu.kit.scc.webreg.service.reg.GroupCapable;
import edu.kit.scc.webreg.service.reg.Infotainment;
import edu.kit.scc.webreg.service.reg.InfotainmentCapable;
import edu.kit.scc.webreg.service.reg.RegisterUserWorkflow;
import edu.kit.scc.webreg.service.reg.ScriptingWorkflow;
import edu.kit.scc.webreg.service.reg.SetPasswordCapable;
import edu.kit.scc.webreg.service.reg.impl.GroupUpdateStructure;
import jcifs.util.Hexdump;
import jcifs.util.MD4;
public class ScriptedLdapRegisterWorkflow
implements RegisterUserWorkflow, InfotainmentCapable, GroupCapable, SetPasswordCapable, ScriptingWorkflow {
protected static Logger logger = LoggerFactory.getLogger(ScriptedLdapRegisterWorkflow.class);
protected ScriptingEnv scriptingEnv;
@Override
public void registerUser(UserEntity user, ServiceEntity service,
RegistryEntity registry, Auditor auditor) throws RegisterException {
reconciliation(user, service, registry, auditor);
}
@Override
public void deregisterUser(UserEntity user, ServiceEntity service,
RegistryEntity registry, Auditor auditor) throws RegisterException {
logger.info("AbstractLdapRegister Deregister user {} for service {}", user.getEppn(), service.getName());
PropertyReader prop = PropertyReader.newRegisterPropReader(service);
Map<String, String> regMap = registry.getRegistryValues();
String localUid = regMap.get("localUid");
LdapWorker ldapWorker = new LdapWorker(prop, auditor, Boolean.parseBoolean(regMap.get("sambaEnabled")));
ldapWorker.deleteUser(localUid);
ldapWorker.closeConnections();
}
@Override
public Boolean updateRegistry(UserEntity user, ServiceEntity service,
RegistryEntity registry, Auditor auditor) throws RegisterException {
PropertyReader prop = PropertyReader.newRegisterPropReader(service);
/*
* Compare values from user and registry store. Found differences trigger
* a full reconsiliation
*/
Map<String, String> reconMap = new HashMap<String, String>();
try {
String scriptName = prop.readProp("script_name");
ScriptEntity scriptEntity = scriptingEnv.getScriptDao().findByName(scriptName);
if (scriptEntity == null)
throw new RegisterException("service not configured properly. script is missing.");
if (scriptEntity.getScriptType().equalsIgnoreCase("javascript")) {
ScriptEngine engine = (new ScriptEngineManager()).getEngineByName(scriptEntity.getScriptEngine());
if (engine == null)
throw new RegisterException("service not configured properly. engine not found: " + scriptEntity.getScriptEngine());
engine.eval(scriptEntity.getScript());
Invocable invocable = (Invocable) engine;
invocable.invokeFunction("updateRegistry", scriptingEnv, reconMap, user, registry, service, auditor, logger);
}
else {
throw new RegisterException("unkown script type: " + scriptEntity.getScriptType());
}
} catch (PropertyReaderException e) {
throw new RegisterException(e);
} catch (ScriptException e) {
throw new RegisterException(e);
} catch (NoSuchMethodException e) {
throw new RegisterException(e);
}
if ("true".equals(prop.readPropOrNull("samba_enabled")))
reconMap.put("sambaEnabled", "true");
else
reconMap.put("sambaEnabled", "false");
Boolean change = false;
for (Entry<String, String> entry : reconMap.entrySet()) {
if (! registry.getRegistryValues().containsKey(entry.getKey())) {
auditor.logAction("", "UPDATE USER REGISTRY", user.getEppn(), "ADD " +
entry.getKey() + ": " + registry.getRegistryValues().get(entry.getKey()) +
" => " + entry.getValue()
, AuditStatus.SUCCESS);
registry.getRegistryValues().put(entry.getKey(), entry.getValue());
change |= true;
}
else if (! registry.getRegistryValues().get(entry.getKey()).equals(entry.getValue())) {
auditor.logAction("", "UPDATE USER REGISTRY", user.getEppn(), "REPLACE " +
entry.getKey() + ": " + registry.getRegistryValues().get(entry.getKey()) +
" => " + entry.getValue()
, AuditStatus.SUCCESS);
registry.getRegistryValues().put(entry.getKey(), entry.getValue());
change |= true;
}
}
return change;
}
@Override
public void reconciliation(UserEntity user, ServiceEntity service,
RegistryEntity registry, Auditor auditor) throws RegisterException {
logger.info("LDAP Reconsiliation user {} for service {}", user.getEppn(), service.getName());
PropertyReader prop = PropertyReader.newRegisterPropReader(service);
Map<String, String> regMap = registry.getRegistryValues();
String cn = regMap.get("cn");
String sn = regMap.get("sn");
String givenName = regMap.get("givenName");
String mail = regMap.get("mail");
String localUid = regMap.get("localUid");
String uidNumber = regMap.get("uidNumber");
String gidNumber = regMap.get("gidNumber");
String homeDir = regMap.get("homeDir");
String description = registry.getId().toString();
LdapWorker ldapWorker = new LdapWorker(prop, auditor, Boolean.parseBoolean(regMap.get("sambaEnabled")));
ldapWorker.reconUser(cn, sn, givenName, mail, localUid, uidNumber, gidNumber, homeDir, description);
if (prop.hasProp("pw_location") &&
((prop.readPropOrNull("pw_location").equalsIgnoreCase("registry")) || prop.readPropOrNull("pw_location").equalsIgnoreCase("both"))
&& (! registry.getRegistryValues().containsKey("userPassword"))) {
List<String> pwList = ldapWorker.getPasswords(localUid);
if (pwList.size() > 0) {
logger.debug("userPassword is not set in registry but in LDAP ({}). Importing from LDAP", pwList.size());
registry.getRegistryValues().put("userPassword", pwList.get(0));
}
}
ldapWorker.closeConnections();
}
@Override
public Infotainment getInfo(RegistryEntity registry, UserEntity user,
ServiceEntity service) throws RegisterException {
Infotainment info = new Infotainment();
PropertyReader prop = PropertyReader.newRegisterPropReader(service);
Map<String, String> regMap = registry.getRegistryValues();
String localUid = regMap.get("localUid");
LdapWorker ldapWorker = new LdapWorker(prop, null, Boolean.parseBoolean(regMap.get("sambaEnabled")));
ldapWorker.getInfo(info, localUid);
ldapWorker.closeConnections();
return info;
}
@Override
public void updateGroups(ServiceEntity service, GroupUpdateStructure updateStruct, Auditor auditor)
throws RegisterException {
PropertyReader prop = PropertyReader.newRegisterPropReader(service);
LdapWorker ldapWorker = new LdapWorker(prop, auditor, Boolean.parseBoolean(prop.readPropOrNull("samba_enabled")));
try {
String scriptName = prop.readProp("script_name");
ScriptEntity scriptEntity = scriptingEnv.getScriptDao().findByName(scriptName);
if (scriptEntity == null)
throw new RegisterException("service not configured properly. script is missing.");
if (scriptEntity.getScriptType().equalsIgnoreCase("javascript")) {
ScriptEngine engine = (new ScriptEngineManager()).getEngineByName(scriptEntity.getScriptEngine());
if (engine == null)
throw new RegisterException("service not configured properly. engine not found: " + scriptEntity.getScriptEngine());
engine.eval(scriptEntity.getScript());
Invocable invocable = (Invocable) engine;
for (GroupEntity group : updateStruct.getGroups()) {
long a = System.currentTimeMillis();
Set<UserEntity> users = updateStruct.getUsersForGroup(group);
logger.debug("Update Ldap Group for group {} and Service {}", group.getName(), service.getName());
Set<String> memberUids = new HashSet<String>(users.size());
Map<String, String> reconMap = new HashMap<String, String>();
for (UserEntity user : users) {
Object result = invocable.invokeFunction("resolveUid", scriptingEnv, reconMap, user, null, service, auditor, logger);
if (result != null) {
memberUids.add(result.toString());
}