diff --git a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerReturnboxController.java b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerReturnboxController.java
index cf820bffd07b3ce9b0fce41d0e0866e573362ff2..b2878c6a5b510d36b52633942e237f8884e41379 100644
--- a/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerReturnboxController.java
+++ b/src/main/java/org/olat/course/nodes/projectbroker/ProjectBrokerReturnboxController.java
@@ -38,11 +38,11 @@ import org.olat.course.run.userview.UserCourseEnvironment;
 
 /**
  *
- * @author Christian Guretzki 
+ * @author Christian Guretzki
  */
 
 public class ProjectBrokerReturnboxController extends ReturnboxController  {
-	
+
 	private Project project;
 
 	/**
@@ -54,13 +54,13 @@ public class ProjectBrokerReturnboxController extends ReturnboxController  {
 	 * @param userCourseEnv
 	 * @param previewMode
 	 */
-	public ProjectBrokerReturnboxController(UserRequest ureq, WindowControl wControl, 
+	public ProjectBrokerReturnboxController(UserRequest ureq, WindowControl wControl,
 			CourseNode node, UserCourseEnvironment userCourseEnv, boolean previewMode, Project project) {
 		super(ureq, wControl, node, userCourseEnv, previewMode, false);
 		this.project = project;
 		initReturnbox(ureq, wControl, node, userCourseEnv, previewMode);
 	}
-	
+
 	/**
 	 * Return returnbox base-path. e.g. course/<COURSE_ID>/returnbox/<NODE_id>/<USER_NAME>
 	 * @see org.olat.course.nodes.ta.ReturnboxController#getReturnboxPathFor(org.olat.course.run.environment.CourseEnvironment, org.olat.course.nodes.CourseNode, org.olat.core.id.Identity)
@@ -71,9 +71,9 @@ public class ProjectBrokerReturnboxController extends ReturnboxController  {
 	}
 
 	/**
-	 * Return returnbox base-path. e.g. course/<COURSE_ID>/returnbox/<NODE_id> 
-	 * To have the path for certain user you must call method 'getReturnboxPathFor'  
-	 * 
+	 * Return returnbox base-path. e.g. course/<COURSE_ID>/returnbox/<NODE_id>
+	 * To have the path for certain user you must call method 'getReturnboxPathFor'
+	 *
 	 * @param project
 	 * @param courseEnv
 	 * @param cNode
@@ -82,5 +82,5 @@ public class ProjectBrokerReturnboxController extends ReturnboxController  {
 	public static String getReturnboxBasePathForProject(Project project, CourseEnvironment courseEnv, CourseNode node) {
 		return getReturnboxPathRelToFolderRoot(courseEnv, node) + File.separator + project.getKey();
 	}
-	
+
 }
diff --git a/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectBrokerManager.java b/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectBrokerManager.java
index e55acf81f0a3b83a27cb3359ce144ea08749b75a..e1ec43b7311fdb95edc5f30087bf51b934848743 100644
--- a/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectBrokerManager.java
+++ b/src/main/java/org/olat/course/nodes/projectbroker/service/ProjectBrokerManager.java
@@ -47,27 +47,27 @@ public interface ProjectBrokerManager {
 	public List<Project> getProjectListBy(Long projectBrokerId);
 
 	public List<Project> getProjectsWith(BusinessGroup group);
-	
-	
+
+
 	/**
 	 * Returns a project-broker object for certain project-broker-ID.
 	 * @param projectBrokerId
 	 * @return
 	 */
 	public ProjectBroker getProjectBroker(Long projectBrokerId);
-	
+
 	/**
-	 * Creates a new project-broker and save it. 
+	 * Creates a new project-broker and save it.
 	 * @return
 	 */
 	public ProjectBroker createAndSaveProjectBroker();
-	
+
 	/**
 	 * Update and save an existing project.
 	 * @param project
 	 */
 	public void updateProject(Project project);
-	
+
 	/**
 	 * Create and save a new project.
 	 * @param projectTitle
@@ -122,11 +122,11 @@ public interface ProjectBrokerManager {
 	 * @param project
 	 * @param moduleConfig
 	 * @param nbrSelectedProjects
-	 * @param isParticipantInAnyProject 
+	 * @param isParticipantInAnyProject
 	 * @return
 	 */
 	public boolean canBeProjectSelectedBy(Identity identity, Project project,  ProjectBrokerModuleConfiguration moduleConfig, int nbrSelectedProjects, boolean isParticipantInAnyProject);
-	
+
 	/**
 	 * Return true when the project can be de-selected by an identity.
 	 * @param identity
@@ -171,17 +171,17 @@ public interface ProjectBrokerManager {
 	 * @param cNode
 	 */
 	public void saveAttachedFile(Project project, String fileName, VFSLeaf uploadedItem, CourseEnvironment courseEnv, CourseNode cNode);
-	
+
 	/**
-	 * Return true when the custom-field value is one of possible-values (drop-down-mode) or when it could be any value (input field). 
+	 * Return true when the custom-field value is one of possible-values (drop-down-mode) or when it could be any value (input field).
 	 * @param value
 	 * @param string
 	 * @return
 	 */
 	public boolean isCustomFieldValueValid(String value, String string);
-	
+
 	/**
-	 * Get attachment-file relative path. 
+	 * Get attachment-file relative path.
 	 * E.g. course/<COURSE_ID>/projectbroker_attach/<COURSE_NODE>/<PROJECT_ID>
 	 * @param project
 	 * @param courseEnv
@@ -228,7 +228,7 @@ public interface ProjectBrokerManager {
 	 * @return
 	 */
 	public boolean existProjectName(Long projectBrokerId, String newProjectTitle);
-	
+
 	/**
 	 * Get attachment-folder relative path (without project-id). THis path can be used to delete all
 	 * attachment-file for certain project-broker.
@@ -240,7 +240,7 @@ public interface ProjectBrokerManager {
 	public String getAttachmentBasePathRelToFolderRoot(CourseEnvironment courseEnvironment, CourseNode courseNode);
 
 	/**
-	 * Get list of selected projects for certain identity. 
+	 * Get list of selected projects for certain identity.
 	 * @param identity
 	 * @param projectBrokerId
 	 * @return
@@ -255,7 +255,7 @@ public interface ProjectBrokerManager {
 	public Project getProject(Long projectId);
 
 	/**
-	 * Get list of coached projects for certain identity. 
+	 * Get list of coached projects for certain identity.
 	 * @param identity
 	 * @param projectBrokerId
 	 * @return
@@ -268,5 +268,5 @@ public interface ProjectBrokerManager {
 	 * @return
 	 */
 	public boolean existsProject(Long projectKey);
-	
+
 }
diff --git a/src/main/java/org/olat/repository/RepositoryManager.java b/src/main/java/org/olat/repository/RepositoryManager.java
index 709d1b6a71279172147df7c1ff4c30f1b9e3c0a6..74ad472d1b8d0acbe2bacef65e0c7591d770bfd8 100644
--- a/src/main/java/org/olat/repository/RepositoryManager.java
+++ b/src/main/java/org/olat/repository/RepositoryManager.java
@@ -88,6 +88,7 @@ import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceManager;
 import org.olat.resource.accesscontrol.ResourceReservation;
 import org.olat.resource.accesscontrol.manager.ACReservationDAO;
+import org.olat.resource.accesscontrol.provider.auto.AutoAccessManager;
 import org.olat.search.service.document.RepositoryEntryDocument;
 import org.olat.search.service.indexer.LifeFullIndexer;
 import org.olat.user.UserImpl;
@@ -99,15 +100,15 @@ import org.springframework.stereotype.Service;
  * Initial Date:  Mar 31, 2004
  *
  * @author Mike Stock
- * 
- * Comment:  
- * 
+ *
+ * Comment:
+ *
  */
 @Service("repositoryManager")
 public class RepositoryManager {
-	
+
 	private static final OLog log = Tracing.createLoggerFor(RepositoryManager.class);
-	
+
 	public static final int PICTURE_WIDTH = 570;
 	public static final int PICTURE_HEIGHT = (PICTURE_WIDTH / 3) * 2;
 
@@ -125,25 +126,27 @@ public class RepositoryManager {
 	private ACReservationDAO reservationDao;
 	@Autowired
 	private LifeFullIndexer lifeIndexer;
+	@Autowired
+	private AutoAccessManager autoAccessManager;
 
 	/**
 	 * @return Singleton.
 	 */
-	public static RepositoryManager getInstance() { 
+	public static RepositoryManager getInstance() {
 		return CoreSpringFactory.getImpl(RepositoryManager.class);
 	}
-	
+
 	/**
 	 * @param repositoryEntryStatusCode
 	 */
 	public RepositoryEntryStatus createRepositoryEntryStatus(int repositoryEntryStatusCode) {
 		return new RepositoryEntryStatus(repositoryEntryStatusCode);
 	}
-	
+
 	/**
 	 * Copy the repo entry image from the source to the target repository entry.
 	 * If the source repo entry does not exists, nothing will happen
-	 * 
+	 *
 	 * @param src
 	 * @param target
 	 * @return
@@ -158,16 +161,16 @@ public class RepositoryManager {
 		if(targetFile != null) {
 			targetFile.delete();
 		}
-		
+
 		String sourceImageSuffix = FileUtils.getFileSuffix(srcFile.getName());
 		VFSContainer repositoryHome = new LocalFolderImpl(new File(FolderConfig.getCanonicalRepositoryHome()));
-		VFSLeaf newImage = repositoryHome.createChildLeaf(target.getResourceableId() + "." + sourceImageSuffix);	
+		VFSLeaf newImage = repositoryHome.createChildLeaf(target.getResourceableId() + "." + sourceImageSuffix);
 		if (newImage != null) {
 			return VFSManager.copyContent(srcFile, newImage);
 		}
 		return false;
 	}
-	
+
 	public void deleteImage(RepositoryEntry re) {
 		VFSLeaf imgFile =  getImage(re);
 		if (imgFile != null) {
@@ -180,7 +183,7 @@ public class RepositoryManager {
 			imgFile.delete();
 		}
 	}
-	
+
 	public VFSLeaf getImage(OLATResourceable re) {
 		VFSContainer repositoryHome = new LocalFolderImpl(new File(FolderConfig.getCanonicalRepositoryHome()));
 		String imageName = re.getResourceableId() + ".jpg";
@@ -195,7 +198,7 @@ public class RepositoryManager {
 		}
 		return null;
 	}
-	
+
 	public boolean setImage(VFSLeaf newImageFile, RepositoryEntry re) {
 		VFSLeaf currentImage = getImage(re);
 		if(currentImage != null) {
@@ -207,20 +210,20 @@ public class RepositoryManager {
 			}
 			currentImage.delete();
 		}
-		
+
 		if(newImageFile == null || !newImageFile.exists() || newImageFile.getSize() <= 0) {
 			return false;
 		}
-		
+
 		String targetExtension = ".png";
 		String extension = FileUtils.getFileSuffix(newImageFile.getName());
 		if("jpg".equalsIgnoreCase(extension) || "jpeg".equalsIgnoreCase(extension)) {
 			targetExtension = ".jpg";
 		}
-		
+
 		VFSContainer repositoryHome = new LocalFolderImpl(new File(FolderConfig.getCanonicalRepositoryHome()));
 		VFSLeaf repoImage = repositoryHome.createChildLeaf(re.getResourceableId() + targetExtension);
-		
+
 		if(targetExtension.equals(".png") || targetExtension.equals(".jpg")) {
 			Size newImageSize = imageHelper.getSize(newImageFile, extension);
 			if(newImageSize != null && newImageSize.getWidth() <= PICTURE_WIDTH && newImageSize.getHeight() <= PICTURE_HEIGHT) {
@@ -233,7 +236,7 @@ public class RepositoryManager {
 		return size != null;
 	}
 
-	
+
 	/**
 	 * Lookup repo entry by key.
 	 * @param the repository entry key (not the olatresourceable key)
@@ -243,7 +246,7 @@ public class RepositoryManager {
 		if (key == null) return null;
 		return lookupRepositoryEntry(key, false) ;
 	}
-	
+
 	/**
 	 * Lookup repo entry by key.
 	 * @param the repository entry key (not the olatresourceable key)
@@ -264,13 +267,13 @@ public class RepositoryManager {
 		}
 		return entries.get(0);
 	}
-	
+
 	public OLATResource lookupRepositoryEntryResource(Long key) {
 		if (key == null) return null;
 		StringBuilder query = new StringBuilder();
 		query.append("select v.olatResource from ").append(RepositoryEntry.class.getName()).append(" as v ")
 		     .append(" where v.key = :repoKey");
-		
+
 		List<OLATResource> entries = dbInstance.getCurrentEntityManager()
 				.createQuery(query.toString(), OLATResource.class)
 				.setParameter("repoKey", key)
@@ -340,7 +343,7 @@ public class RepositoryManager {
 		}
 		return result.get(0);
 	}
-	
+
 	public Long lookupRepositoryEntryKey(OLATResourceable resourceable, boolean strict) {
 		OLATResource ores = (resourceable instanceof OLATResource) ? (OLATResource)resourceable
 				: OLATResourceManager.getInstance().findResourceable(resourceable);
@@ -390,12 +393,12 @@ public class RepositoryManager {
 		  .append(" inner join fetch v.statistics as statistics")
 		  .append(" left join fetch v.lifecycle as lifecycle")
 		  .append(" where v.softkey=:softkey");
-		
+
 		List<RepositoryEntry> result = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("softkey", softkey)
 				.getResultList();
-		
+
 		int size = result.size();
 		if (strict) {
 			if (size != 1)
@@ -410,7 +413,7 @@ public class RepositoryManager {
 		}
 		return result.get(0);
 	}
-	
+
 	/**
 	 * Convenience method to access the repositoryEntry displayname by the referenced OLATResourceable id.
 	 * This only works if a repository entry has an referenced olat resourceable like a course or an content package repo entry
@@ -458,7 +461,7 @@ public class RepositoryManager {
 		else if (displaynames.isEmpty()) return null;
 		return displaynames.get(0);
 	}
-	
+
     /**
      * Check if (and which) external IDs already exist.
      * @param a collection of external IDs to check if already existing
@@ -491,7 +494,7 @@ public class RepositoryManager {
 		sb.append("select v from ").append(RepositoryEntryShortImpl.class.getName()).append(" v ")
 		  .append(" inner join fetch v.olatResource as ores")
 		  .append(" where ores.key in (:resKeys)");
-		
+
 		List<Long> resourceKeys = PersistenceHelper.toKeys(resources);
 		List<RepositoryEntryShort> shorties = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntryShort.class)
@@ -499,7 +502,7 @@ public class RepositoryManager {
 				.getResultList();
 		return shorties;
 	}
-	
+
 	/**
 	 * Load a list of repository entry without all the security groups ...
 	 * @param resources
@@ -513,7 +516,7 @@ public class RepositoryManager {
 				.getResultList();
 		return shorties;
 	}
-	
+
 	/**
 	 * Test a repo entry if identity is allowed to launch.
 	 * @param ureq
@@ -523,7 +526,7 @@ public class RepositoryManager {
 	public boolean isAllowedToLaunch(UserRequest ureq, RepositoryEntry re) {
 		return isAllowedToLaunch(ureq.getIdentity(), ureq.getUserSession().getRoles(), re);
 	}
-	
+
 	public RepositoryEntrySecurity isAllowed(UserRequest ureq, RepositoryEntry re) {
 		return isAllowed(ureq.getIdentity(), ureq.getUserSession().getRoles(), re);
 	}
@@ -558,10 +561,10 @@ public class RepositoryManager {
 		} else if (re.getAccess() == RepositoryEntry.ACC_OWNERS && re.isMembersOnly()) {
 			return repositoryEntryRelationDao.isMember(identity, re);
 		}
-		
+
 		return false;
 	}
-	
+
 	public RepositoryEntrySecurity isAllowed(Identity identity, Roles roles, RepositoryEntry re) {
 		boolean isOwner = false;
 		boolean isCourseCoach = false;
@@ -569,10 +572,10 @@ public class RepositoryManager {
 		boolean isCourseParticipant = false;
 		boolean isGroupParticipant = false;
 		boolean isGroupWaiting = false;
-		
+
 		boolean isEntryAdmin = false;
 		boolean canLaunch = false;
-		
+
 		if (roles.isGuestOnly()) {
 			if (re.getAccess() >= RepositoryEntry.ACC_USERS_GUESTS) {
 				// allow for guests if access granted for guests
@@ -612,10 +615,10 @@ public class RepositoryManager {
 						break;
 					}
 					case invitee: break;
-				
+
 				}
 			}
-			
+
 			if(isOwner) {
 				canLaunch = true;
 				isEntryAdmin = true;
@@ -647,9 +650,9 @@ public class RepositoryManager {
 				}
 			}
 		}
-		
+
 		boolean readOnly = new RepositoryEntryStatus(re.getStatusCode()).isClosed();
-		
+
 		return new RepositoryEntrySecurity(isEntryAdmin, isOwner,
 				isCourseParticipant, isCourseCoach,
 				isGroupParticipant, isGroupCoach,
@@ -669,7 +672,7 @@ public class RepositoryManager {
 		lifeIndexer.indexDocument(RepositoryEntryDocument.TYPE, updatedRe.getKey());
 		return updatedRe;
 	}
-	
+
 	public RepositoryEntry setAccessAndProperties(final RepositoryEntry re,
 			int access, boolean membersOnly,
 			boolean canCopy, boolean canReference, boolean canDownload) {
@@ -686,17 +689,17 @@ public class RepositoryManager {
 		reloadedRe.setCanReference(canReference);
 		reloadedRe.setCanDownload(canDownload);
 		RepositoryEntry updatedRe = dbInstance.getCurrentEntityManager().merge(reloadedRe);
-		
+
 		//fetch the values
 		updatedRe.getStatistics().getLaunchCounter();
 		if(updatedRe.getLifecycle() != null) {
 			updatedRe.getLifecycle().getKey();
 		}
-		
+
 		dbInstance.commit();
 		return updatedRe;
 	}
-	
+
 	public RepositoryEntry setLeaveSetting(final RepositoryEntry re,
 			RepositoryEntryAllowToLeaveOptions setting) {
 		RepositoryEntry reloadedRe = repositoryEntryDao.loadForUpdate(re);
@@ -711,8 +714,8 @@ public class RepositoryManager {
 		}
 		dbInstance.commit();
 		return updatedRe;
-	} 
-	
+	}
+
 	/**
 	 * This method doesn't update empty and null values! ( Reserved to unit tests
 	 * and REST API)
@@ -732,7 +735,7 @@ public class RepositoryManager {
 		if(reloadedRe == null) {
 			return null;
 		}
-		
+
 		if(StringHelper.containsNonWhitespace(displayName)) {
 			reloadedRe.setDisplayname(displayName);
 		}
@@ -757,11 +760,11 @@ public class RepositoryManager {
 				reloadedRe.setAllowToLeaveOption(RepositoryEntryAllowToLeaveOptions.never);
 			}
 		}
-		
+
 		RepositoryEntryLifecycle cycleToDelete = null;
 		RepositoryEntryLifecycle currentCycle = reloadedRe.getLifecycle();
 		if(currentCycle != null) {
-			// currently, it's a private cycle 
+			// currently, it's a private cycle
 			if(currentCycle.isPrivateCycle()) {
 				//the new one is none or public, remove the private cycle
 				if(cycle == null || !cycle.isPrivateCycle()) {
@@ -775,12 +778,13 @@ public class RepositoryManager {
 		if(cycleToDelete != null) {
 			dbInstance.getCurrentEntityManager().remove(cycleToDelete);
 		}
-		
+
 		dbInstance.commit();
 		lifeIndexer.indexDocument(RepositoryEntryDocument.TYPE, updatedRe.getKey());
+		autoAccessManager.grantAccess(updatedRe);
 		return updatedRe;
 	}
-	
+
 	/**
 	 * The method updates empty and null values!
 	 * @param re
@@ -818,7 +822,7 @@ public class RepositoryManager {
 		RepositoryEntryLifecycle cycleToDelete = null;
 		RepositoryEntryLifecycle currentCycle = reloadedRe.getLifecycle();
 		if(currentCycle != null) {
-			// currently, it's a private cycle 
+			// currently, it's a private cycle
 			if(currentCycle.isPrivateCycle()) {
 				//the new one is none or public, remove the private cycle
 				if(cycle == null || !cycle.isPrivateCycle()) {
@@ -832,22 +836,23 @@ public class RepositoryManager {
 		if(cycleToDelete != null) {
 			dbInstance.getCurrentEntityManager().remove(cycleToDelete);
 		}
-		
+
 		//fetch the values
 		updatedRe.getStatistics().getLaunchCounter();
 		if(updatedRe.getLifecycle() != null) {
 			updatedRe.getLifecycle().getKey();
 		}
-		
+
 		dbInstance.commit();
 		lifeIndexer.indexDocument(RepositoryEntryDocument.TYPE, updatedRe.getKey());
+		autoAccessManager.grantAccess(updatedRe);
 		return updatedRe;
 	}
-	
+
 	public void triggerIndexer(RepositoryEntryRef re) {
 		lifeIndexer.indexDocument(RepositoryEntryDocument.TYPE, re.getKey());
 	}
-	
+
 	/**
 	 * Count by type, limit by role accessability.
 	 * @param restrictedType
@@ -866,14 +871,14 @@ public class RepositoryManager {
 		dbquery.setCacheable(true);
 		return ((Long)dbquery.list().get(0)).intValue();
 	}
-	
+
 	public long countPublished(String restrictedType) {
 		StringBuilder query = new StringBuilder(400);
 		query.append("select count(*) from org.olat.repository.RepositoryEntry v")
 		     .append(" inner join v.olatResource res")
 		     .append(" where res.resName=:restrictedType ")
 		     .append(" and ((v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true) or v.access>=").append(RepositoryEntry.ACC_USERS).append(")");
-		
+
 		List<Number> count = dbInstance.getCurrentEntityManager()
 				.createQuery(query.toString(), Number.class)
 				.setParameter("restrictedType", restrictedType)
@@ -893,14 +898,14 @@ public class RepositoryManager {
 		if(roles.isOLATAdmin()) {
 			identity = null;//not need for the query as administrator
 		}
-		
+
 		StringBuilder sb = new StringBuilder(400);
 		sb.append("select distinct v from ").append(RepositoryEntry.class.getName()).append(" v ")
 		  .append(" inner join fetch v.olatResource as res")
 		  .append(" inner join fetch v.statistics as statistics")
 		  .append(" left join fetch v.lifecycle as lifecycle")
 		  .append(" where res.resName in (:restrictedType) and ");
-		
+
 		boolean setIdentity = false;
 		if (roles.isOLATAdmin()) {
 			sb.append("v.access>=").append(RepositoryEntry.ACC_OWNERS); // treat admin special b/c admin is author as well
@@ -926,7 +931,7 @@ public class RepositoryManager {
 	 */
 	public List<RepositoryEntry> queryByTypeLimitAccess(Identity identity, Roles roles, List<String> restrictedType) {
 		if(restrictedType == null | restrictedType.isEmpty()) return Collections.emptyList();
-		
+
 		String institution = identity.getUser().getProperty(UserConstants.INSTITUTIONALNAME, null);
 		List<RepositoryEntry> results = new ArrayList<RepositoryEntry>();
 		if(!roles.isOLATAdmin() && institution != null && institution.length() > 0 && roles.isInstitutionalResourceManager()) {
@@ -942,7 +947,7 @@ public class RepositoryManager {
 			  .append(" inner join identity.user as user")
 			  .append(" where user.institutionalName=:institutionCourseManager")
 			  .append(" and res.resName in (:restrictedType) and v.access = 1");
-			
+
 			List<RepositoryEntry> institutionalResults = dbInstance.getCurrentEntityManager()
 					.createQuery(sb.toString(), RepositoryEntry.class)
 					.setParameter("restrictedType", restrictedType)
@@ -950,12 +955,12 @@ public class RepositoryManager {
 					.getResultList();
 			results.addAll(institutionalResults);
 		}
-		
+
 		long start = System.currentTimeMillis();
 		List<RepositoryEntry> genericResults = queryByTypeLimitAccess(identity, restrictedType, roles);
 		long timeQuery3 = System.currentTimeMillis() - start;
 		log.info("Repo-Perf: queryByTypeLimitAccess#3 takes " + timeQuery3);
-		
+
 		if(results.isEmpty()) {
 			results.addAll(genericResults);
 		} else {
@@ -970,7 +975,7 @@ public class RepositoryManager {
 
 	/**
 	 * Query by ownership, optionally limit by type.
-	 * 
+	 *
 	 * @param identity
 	 * @param limitType
 	 * @return Results
@@ -989,7 +994,7 @@ public class RepositoryManager {
 		if (limitTypes != null && limitTypes.length > 0) {
 			sb.append(" and res.resName in (:types)");
 		}
-		
+
 		TypedQuery<RepositoryEntry> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("identityKey", identity.getKey());
@@ -1002,11 +1007,11 @@ public class RepositoryManager {
 		}
 		return query.getResultList();
 	}
-	
+
 	/**
 	 * Return the entries visible by the specified roles as member. The query
 	 * take the business groups in account.
-	 * 
+	 *
 	 * @param identity
 	 * @param owner
 	 * @param coach
@@ -1017,7 +1022,7 @@ public class RepositoryManager {
 	public List<RepositoryEntry> queryByMembership(IdentityRef identity, boolean owner, boolean coach, boolean participant, String... limitTypes) {
 		if (identity == null) throw new AssertException("identity can not be null!");
 		if (!owner && !coach && !participant) return Collections.emptyList();
-		
+
 		StringBuilder sb = new StringBuilder(512);
 		sb.append("select v from repositoryentry v ")
 		  .append(" inner join fetch v.olatResource as res ")
@@ -1045,11 +1050,11 @@ public class RepositoryManager {
 			sb.append(")");
 		}
 		sb.append(")");
-		
+
 		if (limitTypes != null && limitTypes.length > 0) {
 			sb.append(" and res.resName in (:types)");
 		}
-		
+
 		TypedQuery<RepositoryEntry> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("identityKey", identity.getKey());
@@ -1080,7 +1085,7 @@ public class RepositoryManager {
 	/**
 	 * Search for resources that can be referenced by an author. This is the case:
 	 * 1) the user is the owner of the resource
-	 * 2) the user is author and the resource is at least visible to authors (BA) 
+	 * 2) the user is author and the resource is at least visible to authors (BA)
 	 *    and the resource is set to canReference
 	 * @param identity The user initiating the query
 	 * @param roles The current users role set
@@ -1089,7 +1094,7 @@ public class RepositoryManager {
 	 * @param author Limit search to this user (Name, firstname, loginname). Can be NULL
 	 * @param desc Limit search to description. Can be NULL
 	 * @return List of repository entries
-	 */	
+	 */
 	public List<RepositoryEntry> queryReferencableResourcesLimitType(Identity identity, Roles roles, List<String> resourceTypes,
 			String displayName, String author, String desc) {
 		if (identity == null) {
@@ -1101,11 +1106,11 @@ public class RepositoryManager {
 		}
 		return queryResourcesLimitType(identity, resourceTypes, displayName, author, desc, true, false);
 	}
-	
+
 	/**
 	 * Search for resources that can be copied by an author. This is the case:
 	 * 1) the user is the owner of the resource
-	 * 2) the user is author and the resource is at least visible to authors (BA) 
+	 * 2) the user is author and the resource is at least visible to authors (BA)
 	 *    and the resource is set to canCopy
 	 * @param identity The user initiating the query
 	 * @param roles The current users role set
@@ -1114,7 +1119,7 @@ public class RepositoryManager {
 	 * @param author Limit search to this user (Name, firstname, loginname). Can be NULL
 	 * @param desc Limit search to description. Can be NULL
 	 * @return List of repository entries
-	 */	
+	 */
 	public List<RepositoryEntry> queryCopyableResourcesLimitType(Identity identity, Roles roles, List<String> resourceTypes,
 			String displayName, String author, String desc) {
 		if (identity == null) {
@@ -1126,25 +1131,25 @@ public class RepositoryManager {
 		}
 		return queryResourcesLimitType(identity, resourceTypes, displayName, author, desc, false, true);
 	}
-		
+
 	public List<RepositoryEntry> queryResourcesLimitType(Identity identity, List<String> resourceTypes,
 			String displayName, String author, String desc, boolean checkCanReference, boolean checkCanCopy) {
-			
+
 		// cleanup some data: use null values if emtpy
 		if (resourceTypes != null && resourceTypes.size() == 0) resourceTypes = null;
 		if ( ! StringHelper.containsNonWhitespace(displayName)) displayName = null;
 		if ( ! StringHelper.containsNonWhitespace(author)) author = null;
 		if ( ! StringHelper.containsNonWhitespace(desc)) desc = null;
-			
+
 		// Build the query
-		// 1) Joining tables 
+		// 1) Joining tables
 		StringBuilder query = new StringBuilder(400);
 		query.append("select distinct v from ").append(RepositoryEntry.class.getName()).append(" v ")
 		     .append(" inner join fetch v.olatResource as res" )
 			 .append(" inner join fetch v.statistics as statistics")
 		     .append(" left join fetch v.lifecycle as lifecycle");
 		// 2) where clause
-		query.append(" where "); 
+		query.append(" where ");
 		// restrict on ownership or referencability flag
 
 		int access;
@@ -1166,7 +1171,7 @@ public class RepositoryManager {
 			access = RepositoryEntry.ACC_OWNERS;
 			query.append(" v.access>=:access ");
 		}
-		
+
 		// restrict on type
 		if (resourceTypes != null) {
 			query.append(" and res.resName in (:resourcetypes)");
@@ -1194,7 +1199,7 @@ public class RepositoryManager {
 			desc = '%' + desc + '%';
 			query.append(" and v.description like :desc");
 		}
-		
+
 		// create query an set query data
 		TypedQuery<RepositoryEntry> dbquery = dbInstance.getCurrentEntityManager()
 				.createQuery(query.toString(), RepositoryEntry.class);
@@ -1214,13 +1219,13 @@ public class RepositoryManager {
 		if (resourceTypes != null) {
 			dbquery.setParameter("resourcetypes", resourceTypes);
 		}
-		return dbquery.getResultList();		
+		return dbquery.getResultList();
 	}
 
-	
+
 	/**
 	 * Query by ownership, limit by access.
-	 * 
+	 *
 	 * @param identity
 	 * @param limitAccess
 	 * @return Results
@@ -1239,15 +1244,15 @@ public class RepositoryManager {
 			sb.append(" or (v.access=1 and v.membersOnly=true)");
 		}
 		sb.append(")");
-		
+
 		List<RepositoryEntry> entries = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("identityKey", identity.getKey())
 				.setParameter("limitAccess", limitAccess)
 				.getResultList();
-		return entries;		
+		return entries;
 	}
-	
+
 	/**
 	 * check ownership of identity for a resource
 	 * @return true if the identity is member of the security group of the repository entry
@@ -1258,7 +1263,7 @@ public class RepositoryManager {
 		}
 		return repositoryEntryRelationDao.hasRole(identity, entry, GroupRoles.owner.name());
 	}
-	
+
 	/**
 	 * This query need the repository entry as v, v.olatResource as res
 	 * and v.baseGroup as baseGroup
@@ -1276,7 +1281,7 @@ public class RepositoryManager {
 		} else {
 			sb.append(RepositoryEntry.ACC_USERS);
 		}
-		
+
 		//+ membership
 		boolean setIdentity = false;
 		if(!roles.isGuestOnly() && identity != null) {
@@ -1293,15 +1298,15 @@ public class RepositoryManager {
 		sb.append(")");
 		return setIdentity;
 	}
-	
+
 	public int countGenericANDQueryWithRolesRestriction(SearchRepositoryEntryParameters params) {
 		TypedQuery<Number> dbQuery = createGenericANDQueryWithRolesRestriction(params, false, Number.class);
 		Number count = dbQuery.getSingleResult();
 		return count.intValue();
 	}
-	
+
 	public List<RepositoryEntry> genericANDQueryWithRolesRestriction(SearchRepositoryEntryParameters params, int firstResult, int maxResults, boolean orderBy) {
-		
+
 		TypedQuery<RepositoryEntry> dbQuery = createGenericANDQueryWithRolesRestriction(params, orderBy, RepositoryEntry.class);
 		dbQuery.setFirstResult(firstResult);
 		if(maxResults > 0) {
@@ -1310,7 +1315,7 @@ public class RepositoryManager {
 		List<RepositoryEntry> res = dbQuery.getResultList();
 		return res;
 	}
-	
+
 	private <T> TypedQuery<T> createGenericANDQueryWithRolesRestriction(SearchRepositoryEntryParameters params, boolean orderBy, Class<T> type) {
 		String displayName = params.getDisplayName();
 		String author = params.getAuthor();
@@ -1319,14 +1324,14 @@ public class RepositoryManager {
 		final Identity identity = params.getIdentity();
 		final Roles roles = params.getRoles();
 		final String institution = params.getInstitution();
-		
+
 		boolean institut = (!roles.isOLATAdmin() && institution != null && institution.length() > 0 && roles.isInstitutionalResourceManager());
 		boolean var_author = StringHelper.containsNonWhitespace(author);
 		boolean var_displayname = StringHelper.containsNonWhitespace(displayName);
 		boolean var_desc = StringHelper.containsNonWhitespace(desc);
 		boolean var_resourcetypes = (resourceTypes != null && resourceTypes.size() > 0);
 		boolean count = Number.class.equals(type);
-		
+
 		StringBuilder query = new StringBuilder();
 		if(count) {
 			query.append("select count(v.key) from ").append(RepositoryEntry.class.getName()).append(" v ");
@@ -1367,7 +1372,7 @@ public class RepositoryManager {
 	             .append("    where rel.entry=v and rel.group=baseGroup and membership.group=baseGroup and membership.identity=identity and user.identity.key=identity.key")
 	             .append("      and user.institutionalName=:institution and membership.role='").append(GroupRoles.owner.name()).append("'")
 	             .append(")))");
-			
+
 		} else if (params.isOnlyOwnedResources()) {
 			query.append(" where (v.access>0 and exists (select rel from repoentrytogroup as rel, bgroup as baseGroup, bgroupmember as membership  ")
 		         .append("    where rel.entry=v and rel.group=baseGroup and membership.group=baseGroup")
@@ -1383,17 +1388,17 @@ public class RepositoryManager {
 			     .append("      and membership.role in ('").append(GroupRoles.owner.name()).append("','").append(GroupRoles.coach.name()).append("','").append(GroupRoles.participant.name()).append("')")
 			     .append("  )")
 			     .append(" ))");
-			
+
 			setIdentity = true;
 		} else {
 			query.append(" where ");
 			setIdentity = appendAccessSubSelects(query, identity, roles);
 		}
-		
+
 		if(params.getParentEntry() != null) {
 			query.append(" and parentCei.key=:parentCeiKey");
 		}
-		
+
 		if (var_author) { // fuzzy author search
 			author = PersistenceHelper.makeFuzzyQueryString(author);
 
@@ -1409,7 +1414,7 @@ public class RepositoryManager {
 			PersistenceHelper.appendFuzzyLike(query, "identity.name", "author", dbInstance.getDbVendor());
 			query.append(" ))");
 		}
-		
+
 		if (var_displayname) {
 			//displayName = '%' + displayName.replace('*', '%') + '%';
 			//query.append(" and v.displayname like :displayname");
@@ -1417,7 +1422,7 @@ public class RepositoryManager {
 			query.append(" and ");
 			PersistenceHelper.appendFuzzyLike(query, "v.displayname", "displayname", dbInstance.getDbVendor());
 		}
-		
+
 		if (var_desc) {
 			//desc = '%' + desc.replace('*', '%') + '%';
 			//query.append(" and v.description like :desc");
@@ -1425,15 +1430,15 @@ public class RepositoryManager {
 			query.append(" and ");
 			PersistenceHelper.appendFuzzyLike(query, "v.description", "desc", dbInstance.getDbVendor());
 		}
-		
+
 		if (var_resourcetypes) {
 			query.append(" and res.resName in (:resourcetypes)");
 		}
-		
+
 		if(params.getRepositoryEntryKeys() != null && !params.getRepositoryEntryKeys().isEmpty()) {
 			query.append(" and v.key in (:entryKeys)");
 		}
-		
+
 		if(params.getManaged() != null) {
 			if(params.getManaged().booleanValue()) {
 				query.append(" and v.managedFlagsString is not null");
@@ -1441,15 +1446,15 @@ public class RepositoryManager {
 				query.append(" and v.managedFlagsString is null");
 			}
 		}
-		
+
 		if(StringHelper.containsNonWhitespace(params.getExternalId())) {
 			query.append(" and v.externalId=:externalId");
 		}
-		
+
 		if(StringHelper.containsNonWhitespace(params.getExternalRef())) {
 			query.append(" and v.externalRef=:externalRef");
 		}
-		
+
 		if(params.getMarked() != null) {
 			setIdentity = true;
 			query.append(" and v.key ").append(params.getMarked().booleanValue() ? "" : "not").append(" in (")
@@ -1461,7 +1466,7 @@ public class RepositoryManager {
 		if(!count && orderBy) {
 			query.append(" order by v.displayname, v.key ASC");
 		}
-		
+
 		TypedQuery<T> dbQuery = dbInstance.getCurrentEntityManager().createQuery(query.toString(), type);
 		if(institut) {
 			dbQuery.setParameter("institution", institution);
@@ -1496,10 +1501,10 @@ public class RepositoryManager {
 		}
 		return dbQuery;
 	}
-	
+
 	/**
 	 * Leave the course, commit to the database and send events
-	 * 
+	 *
 	 * @param identity
 	 * @param re
 	 * @param status
@@ -1516,7 +1521,7 @@ public class RepositoryManager {
 			sendDeferredEvents(deferredEvents, re);
 		}
 	}
-	
+
 	/**
 	 * add provided list of identities as owners to the repo entry. silently ignore
 	 * if some identities were already owners before.
@@ -1546,7 +1551,7 @@ public class RepositoryManager {
 		}
 		iae.setIdentitiesAddedEvent(reallyAddedId);
 	}
-	
+
 	/**
 	 * remove list of identities as owners of given repository entry.
 	 * @param ureqIdentity
@@ -1556,16 +1561,16 @@ public class RepositoryManager {
 	 */
 	public void removeOwners(Identity ureqIdentity, List<Identity> removeIdentities, RepositoryEntry re){
 		List<RepositoryEntryMembershipModifiedEvent> deferredEvents = new ArrayList<>();
-		
+
 		for (Identity identity : removeIdentities) {
 			removeOwner(ureqIdentity, identity, re);
 			deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(identity, re));
 		}
-		
+
 		dbInstance.commit();
 		sendDeferredEvents(deferredEvents, re);
 	}
-	
+
 	private void sendDeferredEvents(List<? extends MultiUserEvent> events, OLATResourceable ores) {
 		EventBus eventBus = CoordinatorManager.getInstance().getCoordinator().getEventBus();
 		for(MultiUserEvent event:events) {
@@ -1573,7 +1578,7 @@ public class RepositoryManager {
 			eventBus.fireEventToListenersOf(event, OresHelper.lookupType(RepositoryEntry.class));
 		}
 	}
-	
+
 	private void removeOwner(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
 		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.owner.name());
 
@@ -1589,7 +1594,7 @@ public class RepositoryManager {
 		log.audit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
 				+ "' from repositoryentry with key " + re.getKey());
 	}
-	
+
 	public void acceptPendingParticipation(Identity ureqIdentity, Identity identityToAdd, OLATResource resource, ResourceReservation reservation) {
 		RepositoryEntry re = lookupRepositoryEntry(resource, false);
 		if(re != null) {
@@ -1605,7 +1610,7 @@ public class RepositoryManager {
 			reservationDao.deleteReservation(reservation);
 		}
 	}
-	
+
 	/**
 	 * add provided list of identities as tutor to the repo entry. silently ignore
 	 * if some identities were already tutor before.
@@ -1619,7 +1624,7 @@ public class RepositoryManager {
 		List<Identity> reallyAddedId = new ArrayList<Identity>();
 		for (Identity identityToAdd : addIdentities) {
 			if (!repositoryEntryRelationDao.hasRole(identityToAdd, re, GroupRoles.coach.name())) {
-				
+
 				boolean mustAccept = true;
 				if(ureqIdentity != null && ureqIdentity.equals(identityToAdd)) {
 					mustAccept = false;//adding itself, we hope that he knows what he makes
@@ -1628,7 +1633,7 @@ public class RepositoryManager {
 				} else {
 					mustAccept = repositoryModule.isAcceptMembership(ureqRoles);
 				}
-				
+
 				if(mustAccept) {
 					ResourceReservation olderReservation = reservationDao.loadReservation(identityToAdd, re.getOlatResource());
 					if(olderReservation == null) {
@@ -1650,7 +1655,7 @@ public class RepositoryManager {
 		}
 		iae.setIdentitiesAddedEvent(reallyAddedId);
 	}
-	
+
 	/**
 	 * Internal method to add tutors, it makes no check.
 	 * @param ureqIdentity
@@ -1672,7 +1677,7 @@ public class RepositoryManager {
 		log.audit("Identity(.key):" + ureqIdentity.getKey() + " added identity '" + identity.getName()
 				+ "' to repositoryentry with key " + re.getKey());
 	}
-	
+
 	/**
 	 * remove list of identities as tutor of given repository entry.
 	 * @param ureqIdentity
@@ -1689,10 +1694,10 @@ public class RepositoryManager {
 		dbInstance.commit();
 		sendDeferredEvents(deferredEvents, re);
 	}
-	
+
 	private void removeTutor(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
 		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.coach.name());
-		
+
 		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
 		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
 		try{
@@ -1704,7 +1709,7 @@ public class RepositoryManager {
 		log.audit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
 				+ "' from repositoryentry with key " + re.getKey());
 	}
-	
+
 	/**
 	 * add provided list of identities as participant to the repo entry. silently ignore
 	 * if some identities were already participant before.
@@ -1718,7 +1723,7 @@ public class RepositoryManager {
 		List<Identity> reallyAddedId = new ArrayList<Identity>();
 		for (Identity identityToAdd : addIdentities) {
 			if (!repositoryEntryRelationDao.hasRole(identityToAdd, re, GroupRoles.participant.name())) {
-				
+
 				boolean mustAccept = true;
 				if(ureqIdentity != null && ureqIdentity.equals(identityToAdd)) {
 					mustAccept = false;//adding itself, we hope that he knows what he makes
@@ -1727,7 +1732,7 @@ public class RepositoryManager {
 				} else {
 					mustAccept = repositoryModule.isAcceptMembership(ureqRoles);
 				}
-				
+
 				if(mustAccept) {
 					ResourceReservation olderReservation = reservationDao.loadReservation(identityToAdd, re.getOlatResource());
 					if(olderReservation == null) {
@@ -1749,7 +1754,7 @@ public class RepositoryManager {
 		}
 		iae.setIdentitiesAddedEvent(reallyAddedId);
 	}
-	
+
 	/**
 	 * This is for internal usage only. The method dosn't make any check.
 	 * @param ureqIdentity
@@ -1758,7 +1763,7 @@ public class RepositoryManager {
 	 */
 	private void addInternalParticipant(Identity ureqIdentity, Identity identity, RepositoryEntry re) {
 		repositoryEntryRelationDao.addRole(identity, re, GroupRoles.participant.name());
-		
+
 		ActionType actionType = ThreadLocalUserActivityLogger.getStickyActionType();
 		ThreadLocalUserActivityLogger.setStickyActionType(ActionType.admin);
 		try{
@@ -1770,7 +1775,7 @@ public class RepositoryManager {
 		log.audit("Identity(.key):" + ureqIdentity.getKey() + " added identity '" + identity.getName()
 				+ "' to repositoryentry with key " + re.getKey());
 	}
-	
+
 	/**
 	 * remove list of identities as participant of given repository entry.
 	 * @param ureqIdentity
@@ -1787,10 +1792,10 @@ public class RepositoryManager {
 		dbInstance.commit();
 		sendDeferredEvents(deferredEvents, re);
 	}
-	
+
 	private void removeParticipant(Identity ureqIdentity, Identity identity, RepositoryEntry re, MailPackage mailing, boolean sendMail) {
 		repositoryEntryRelationDao.removeRole(identity, re, GroupRoles.participant.name());
-		
+
 		if(sendMail) {
 			RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeParticipant, mailing);
 		}
@@ -1806,11 +1811,11 @@ public class RepositoryManager {
 		log.audit("Identity(.key):" + ureqIdentity.getKey() + " removed identity '" + identity.getName()
 				+ "' from repositoryentry with key " + re.getKey());
 	}
-	
+
 	/**
 	 * Remove the identities as members of the repository and from
 	 * all connected business groups.
-	 * 
+	 *
 	 * @param members
 	 * @param re
 	 */
@@ -1826,7 +1831,7 @@ public class RepositoryManager {
 				ThreadLocalUserActivityLogger.setStickyActionType(actionType);
 			}
 		}
-		
+
 		List<ResourceReservation> reservations = reservationDao.loadReservations(Collections.singletonList(re.getOlatResource()));
 		for(ResourceReservation reservation:reservations) {
 			if(members.contains(reservation.getIdentity())) {
@@ -1848,15 +1853,15 @@ public class RepositoryManager {
 			sendDeferredEvents(deferredEvents, re);
 		}
 		if (allOk) {
-			// do logging - not optimal but 
+			// do logging - not optimal but
 			StringBuilder sb = new StringBuilder();
 			sb.append("Identity(.key):").append(ureqIdentity.getKey()).append("removed multiple identities from security groups. Identities:: " );
 			for (Identity member : members) {
 				sb.append(member.getName()).append(", ");
 			}
-			log.audit(sb.toString());					
+			log.audit(sb.toString());
 		}
-		
+
 		for(Identity identity:members) {
 			RepositoryMailing.sendEmail(ureqIdentity, identity, re, RepositoryMailing.Type.removeParticipant, mailing);
 		}
@@ -1877,11 +1882,11 @@ public class RepositoryManager {
 		if(!StringHelper.containsNonWhitespace(currentUserInstitutionalName)) {
 			return false;
 		}
-		
+
 		if(!roles.isInstitutionalResourceManager()) {
 			return false;
 		}
-		
+
 		StringBuilder sb = new StringBuilder();
 		sb.append("select count(v) from ").append(RepositoryEntry.class.getName()).append(" v")
 		  .append(" inner join v.groups as relGroup")
@@ -1890,7 +1895,7 @@ public class RepositoryManager {
 		  .append(" inner join membership.identity as identity")
 		  .append(" inner join identity.user as user")
 		  .append(" where v.key=:repoKey and user.institutionalName=:institutionCourseManager");
-		
+
 		Number count = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Number.class)
 				.setParameter("repoKey", repositoryEntry.getKey())
@@ -1898,7 +1903,7 @@ public class RepositoryManager {
 				.getSingleResult();
 		return count == null ? false : count.intValue() > 0;
 	}
-	
+
 	public int countLearningResourcesAsStudent(IdentityRef identity) {
 		StringBuilder sb = new StringBuilder(1200);
 		sb.append("select count(v) from ").append(RepositoryEntry.class.getName()).append(" as v ")
@@ -1912,7 +1917,7 @@ public class RepositoryManager {
 				.setParameter("identityKey", identity.getKey())
 				.getSingleResult().intValue();
 	}
-	
+
 	/**
 	 * Gets all learning resources where the user is in a learning group as participant.
 	 * @param identity
@@ -1932,7 +1937,7 @@ public class RepositoryManager {
 		if(StringHelper.containsNonWhitespace(type)) {
 			sb.append(" and res.resName=:resourceType");
 		}
-		
+
 		appendOrderBy(sb, "v", orderby);
 
 		TypedQuery<RepositoryEntry> query = dbInstance.getCurrentEntityManager()
@@ -1948,7 +1953,7 @@ public class RepositoryManager {
 		List<RepositoryEntry> repoEntries = query.getResultList();
 		return repoEntries;
 	}
-	
+
 	/**
 	 * Gets all learning resources where the user is in a learning group as participant.
 	 * @param identity
@@ -1978,7 +1983,7 @@ public class RepositoryManager {
 		}
 		return query.getResultList();
 	}
-	
+
 	public List<RepositoryEntry> getLearningResourcesAsBookmark(Identity identity, Roles roles, String type, int firstResult, int maxResults, RepositoryEntryOrder... orderby) {
 		if(roles.isGuestOnly()) {
 			return Collections.emptyList();
@@ -2025,7 +2030,7 @@ public class RepositoryManager {
 		List<RepositoryEntry> repoEntries = query.getResultList();
 		return repoEntries;
 	}
-	
+
 	public List<RepositoryEntryLight> getParticipantRepositoryEntry(IdentityRef identity, int maxResults, RepositoryEntryOrder... orderby) {
 		StringBuilder sb = new StringBuilder(200);
 		sb.append("select v from repoentrylight as v ")
@@ -2037,7 +2042,7 @@ public class RepositoryManager {
 		  .append(" )")
 		  .append(" and (v.access>=3 or (v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true))");
 		appendOrderBy(sb, "v", orderby);
-		
+
 		TypedQuery<RepositoryEntryLight> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntryLight.class)
 				.setParameter("identityKey", identity.getKey());
@@ -2046,7 +2051,7 @@ public class RepositoryManager {
 		}
 		return query.getResultList();
 	}
-	
+
 	public List<RepositoryEntryLight> getTutorRepositoryEntry(IdentityRef identity, int maxResults, RepositoryEntryOrder... orderby) {
 		StringBuilder sb = new StringBuilder(200);
 		sb.append("select v from repoentrylight as v ")
@@ -2058,7 +2063,7 @@ public class RepositoryManager {
 		  .append(" )")
 		  .append("  and (v.access>=3 or (v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true))");
 		appendOrderBy(sb, "v", orderby);
-		
+
 		TypedQuery<RepositoryEntryLight> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntryLight.class)
 				.setParameter("identityKey", identity.getKey());
@@ -2068,7 +2073,7 @@ public class RepositoryManager {
 
 		return query.getResultList();
 	}
-	
+
 	public int countLearningResourcesAsOwner(IdentityRef identity) {
 		StringBuilder sb = new StringBuilder(200);
 		sb.append("select count(v) from ").append(RepositoryEntry.class.getName()).append(" v ")
@@ -2082,31 +2087,31 @@ public class RepositoryManager {
 				.setParameter("identityKey", identity.getKey())
 				.getSingleResult().intValue();
 	}
-	
+
 	/**
 	 * Gets all learning resources where the user is coach of a learning group or
-	 * where he is in a rights group or where he is in the repository entry owner 
+	 * where he is in a rights group or where he is in the repository entry owner
 	 * group (course administrator)
-	 * 
+	 *
 	 * @param identity
 	 * @return list of RepositoryEntries
 	 */
 	public boolean hasLearningResourcesAsTeacher(IdentityRef identity) {
 		return countLearningResourcesAsTeacher(identity) > 0;
 	}
-	
+
 	public int countLearningResourcesAsTeacher(IdentityRef identity) {
 		StringBuilder sb = new StringBuilder(1200);
 		sb.append("select count(v) from ").append(RepositoryEntry.class.getName()).append(" v ")
 		  .append(" inner join v.olatResource as res ");
 		whereClauseLearningResourcesAsTeacher(sb);
-		
+
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Number.class)
 				.setParameter("identityKey", identity.getKey())
 				.getSingleResult().intValue();
 	}
-	
+
 	public List<RepositoryEntry> getLearningResourcesAsTeacher(Identity identity, int firstResult, int maxResults, RepositoryEntryOrder... orderby) {
 		StringBuilder sb = new StringBuilder(1200);
 		sb.append("select distinct v from ").append(RepositoryEntry.class.getName()).append(" v ")
@@ -2115,7 +2120,7 @@ public class RepositoryManager {
 		  .append(" left join fetch v.lifecycle as lifecycle");
 		whereClauseLearningResourcesAsTeacher(sb);
 		appendOrderBy(sb, "v", orderby);
-		
+
 		TypedQuery<RepositoryEntry> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("identityKey", identity.getKey())
@@ -2126,7 +2131,7 @@ public class RepositoryManager {
 		List<RepositoryEntry> entries = query.getResultList();
 		return entries;
 	}
-	
+
 	/**
 	 * Write the where clause for countLearningResourcesAsTeacher and getLearningResourcesAsTeacher
 	 * @param sb
@@ -2138,7 +2143,7 @@ public class RepositoryManager {
 		  .append(" where (v.access>=3 or (v.access=").append(RepositoryEntry.ACC_OWNERS).append(" and v.membersOnly=true))")
 		  .append(" and membership.identity.key=:identityKey");
 	}
-	
+
 	public int countFavoritLearningResourcesAsTeacher(Identity identity, List<String> types) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select count(v) from ").append(RepositoryEntry.class.getName()).append(" v ")
@@ -2161,7 +2166,7 @@ public class RepositoryManager {
 		}
 		return query.getSingleResult().intValue();
 	}
-	
+
 	public List<RepositoryEntry> getFavoritLearningResourcesAsTeacher(IdentityRef identity, List<String> types, int firstResult, int maxResults,
 			RepositoryEntryOrder... orderby) {
 		StringBuilder sb = new StringBuilder();
@@ -2194,7 +2199,7 @@ public class RepositoryManager {
 		}
 		return query.getResultList();
 	}
-	
+
 	/**
 	 * Need a repository entry or identites to return a list.
 	 * @param re
@@ -2203,7 +2208,7 @@ public class RepositoryManager {
 	 */
 	public List<RepositoryEntryMembership> getRepositoryEntryMembership(RepositoryEntryRef re, Identity... identity) {
 		if(re == null && (identity == null || identity.length == 0)) return Collections.emptyList();
-		
+
 		StringBuilder sb = new StringBuilder(400);
 		sb.append("select distinct membership from repoentrymembership as membership ");
 		boolean and = false;
@@ -2232,30 +2237,30 @@ public class RepositoryManager {
 		List<RepositoryEntryMembership> entries = query.getResultList();
 		return entries;
 	}
-	
+
 	public List<RepositoryEntryMembership> getRepositoryEntryMembership(RepositoryEntryRef re) {
 		if(re == null) return Collections.emptyList();
 
-		StringBuilder sb = new StringBuilder(); 
+		StringBuilder sb = new StringBuilder();
 		sb.append("select membership.identity.key, membership.creationDate, membership.lastModified, membership.role ")
 		  .append(" from ").append(RepositoryEntry.class.getName()).append(" as v ")
 		  .append(" inner join v.groups as relGroup on relGroup.defaultGroup=true")
 		  .append(" inner join relGroup.group as baseGroup")
 		  .append(" inner join baseGroup.members as membership")
 		  .append(" where v.key=:repoKey");
-		
+
 		List<Object[]> members = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Object[].class)
 				.setParameter("repoKey", re.getKey())
 				.getResultList();
-		
+
 		Map<Long, RepositoryEntryMembership> memberships = new HashMap<Long, RepositoryEntryMembership>();
 		for(Object[] membership:members) {
 			Long identityKey = (Long)membership[0];
 			Date lastModified = (Date)membership[1];
 			Date creationDate = (Date)membership[2];
 			Object role = membership[3];
-			
+
 			RepositoryEntryMembership mb = memberships.get(identityKey);
 			if(mb == null) {
 				mb = new RepositoryEntryMembership();
@@ -2265,7 +2270,7 @@ public class RepositoryManager {
 			}
 			mb.setCreationDate(creationDate);
 			mb.setLastModified(lastModified);
-			
+
 			if(GroupRoles.participant.name().equals(role)) {
 				mb.setParticipant(true);
 			} else if(GroupRoles.coach.name().equals(role)) {
@@ -2274,13 +2279,13 @@ public class RepositoryManager {
 				mb.setOwner(true);
 			}
 		}
-		
+
 		return new ArrayList<RepositoryEntryMembership>(memberships.values());
 	}
-	
+
 	public List<RepositoryEntryMembership> getOwnersMembership(List<RepositoryEntry> res) {
 		if(res== null || res.isEmpty()) return Collections.emptyList();
-		
+
 		StringBuilder sb = new StringBuilder(400);
 		sb.append("select distinct membership from ").append(RepositoryEntryMembership.class.getName()).append(" membership ")
 		  .append(" where membership.repoKey in (:repoKey)");
@@ -2293,7 +2298,7 @@ public class RepositoryManager {
 		List<RepositoryEntryMembership> entries = query.getResultList();
 		return entries;
 	}
-	
+
 	public void updateRepositoryEntryMemberships(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re,
 			List<RepositoryEntryPermissionChangeEvent> changes, MailPackage mailing) {
 
@@ -2309,11 +2314,11 @@ public class RepositoryManager {
 		dbInstance.commitAndCloseSession();
 		sendDeferredEvents(deferredEvents, re);
 	}
-	
+
 	private void updateRepositoryEntryMembership(Identity ureqIdentity, Roles ureqRoles, RepositoryEntry re,
 			RepositoryEntryPermissionChangeEvent changes, MailPackage mailing,
 			List<RepositoryEntryMembershipModifiedEvent> deferredEvents) {
-		
+
 		if(changes.getRepoOwner() != null) {
 			if(changes.getRepoOwner().booleanValue()) {
 				addOwners(ureqIdentity, new IdentitiesAddEvent(changes.getMember()), re);
@@ -2322,7 +2327,7 @@ public class RepositoryManager {
 				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
 			}
 		}
-		
+
 		if(changes.getRepoTutor() != null) {
 			if(changes.getRepoTutor().booleanValue()) {
 				addTutors(ureqIdentity, ureqRoles, new IdentitiesAddEvent(changes.getMember()), re, mailing);
@@ -2331,7 +2336,7 @@ public class RepositoryManager {
 				deferredEvents.add(RepositoryEntryMembershipModifiedEvent.removed(changes.getMember(), re));
 			}
 		}
-		
+
 		if(changes.getRepoParticipant() != null) {
 			if(changes.getRepoParticipant().booleanValue()) {
 				addParticipants(ureqIdentity, ureqRoles, new IdentitiesAddEvent(changes.getMember()), re, mailing);
@@ -2341,13 +2346,13 @@ public class RepositoryManager {
 			}
 		}
 	}
-	
+
 	private final boolean and(StringBuilder sb, boolean and) {
 		if(and) sb.append(" and ");
 		else sb.append(" where ");
 		return true;
 	}
-	
+
 	private void appendOrderBy(StringBuilder sb, String var, RepositoryEntryOrder... orderby) {
 		if(orderby != null && orderby.length > 0) {
 			sb.append(" order by ");
@@ -2360,7 +2365,7 @@ public class RepositoryManager {
 			sb.append(var).append(".key asc");
 		}
 	}
-	
+
 	public boolean isIdentityInTutorSecurityGroup(Identity identity, RepositoryEntryRef resource) {
 		return repositoryEntryRelationDao.hasRole(identity, resource, GroupRoles.coach.name());
 	}
diff --git a/src/main/java/org/olat/repository/RepositoryService.java b/src/main/java/org/olat/repository/RepositoryService.java
index 13aa47c236a6f3d1087874a5091edb4a27da0aa5..71075201bb9e27a791849db6ea03d34b3478805e 100644
--- a/src/main/java/org/olat/repository/RepositoryService.java
+++ b/src/main/java/org/olat/repository/RepositoryService.java
@@ -40,68 +40,72 @@ import org.olat.resource.OLATResource;
 
 /**
  * To replace the repository manager
- * 
- * 
+ *
+ *
  * Initial date: 20.02.2014<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
 public interface RepositoryService {
-	
+
 	public static final OLATResourceable REPOSITORY_EVENT_ORES = OresHelper.createOLATResourceableInstance("REPO-CHANGE", 1l);
-	
-	
+
+
 	public RepositoryEntry create(Identity initialAuthor, String initialAuthorAlt,
 			String resourceName, String displayname, String description, OLATResource resource, int access);
-	
+
 	public RepositoryEntry create(String initialAuthor, String resourceName,
 			String displayname, String description, OLATResource resource);
-	
+
 	public RepositoryEntry copy(RepositoryEntry sourceEntry, Identity author, String displayname);
-	
+
 	public RepositoryEntry loadByKey(Long key);
-	
+
 	public RepositoryEntry loadByResourceKey(Long key);
-	
+
+	public List<RepositoryEntry> loadRepositoryEntriesByExternalId(String externalId);
+
+	public List<RepositoryEntry> loadRepositoryEntriesByExternalRef(String externalRef);
+
 	public List<RepositoryEntry> loadByResourceKeys(Collection<Long> keys);
-	
+
 	/**
 	 * @param repositoryEntryKey The key of the repository entry
 	 * @return The olat resource of the repository entry
 	 */
 	public OLATResource loadRepositoryEntryResource(Long repositoryEntryKey);
-	
+
 	/**
 	 * @param softkey The soft key of the repository entry
 	 * @return The olat resource of the repository entry
 	 */
 	public OLATResource loadRepositoryEntryResourceBySoftKey(String softkey);
-	
+
 	public VFSLeaf getIntroductionImage(RepositoryEntry re);
 
 	public VFSLeaf getIntroductionMovie(RepositoryEntry re);
-	
-	
+
+
 	public RepositoryEntry update(RepositoryEntry re);
-	
+
 	/**
 	 * Set the access to 0. The resource is not deleted on the database
 	 * but the resource is removed from the catalog.
-	 * 
-	 * 
+	 *
+	 *
 	 * @param entry
 	 * @param owners If the owners need to be removed
 	 */
 	public RepositoryEntry deleteSoftly(RepositoryEntry entry, Identity deletedBy, boolean owners);
-	
+
 	/**
 	 * The access is set to B.
 	 * @param entry
 	 * @return
 	 */
 	public RepositoryEntry restoreRepositoryEntry(RepositoryEntry entry);
-	
-	
+
+
 	/**
 	 * Delete the learning resource with all its attached resources.
 	 * @param entry
@@ -111,16 +115,16 @@ public interface RepositoryService {
 	 * @return
 	 */
 	public ErrorList deletePermanently(RepositoryEntry entry, Identity identity, Roles roles, Locale locale);
-	
+
 	/**
 	 * Delete only the database object
 	 * @param entry
 	 */
 	public void deleteRepositoryEntryAndBaseGroups(RepositoryEntry entry);
-	
+
 	/**
 	 * This will change the status of the repository entry to "closed" (statusCode=2).
-	 * 
+	 *
 	 * @param entry
 	 * @param identity
 	 * @param roles
@@ -128,56 +132,56 @@ public interface RepositoryService {
 	 * @return The closed repository entry
 	 */
 	public RepositoryEntry closeRepositoryEntry(RepositoryEntry entry);
-	
+
 
 	public RepositoryEntry uncloseRepositoryEntry(RepositoryEntry entry);
-	
+
 	/**
 	 * The unpublish will remove the users (coaches and participants) but will let
 	 * the owners. Catalog entries will be removed and the relations to the business groups
 	 * will be deleted.
-	 * 
+	 *
 	 * @param entry
 	 * @return
 	 */
 	public RepositoryEntry unpublishRepositoryEntry(RepositoryEntry entry);
-	
+
 	/**
 	 * Increment the launch counter and the last usage date.
-	 * 
+	 *
 	 * @param re The repository entry
 	 */
 	public void incrementLaunchCounter(RepositoryEntry re);
-	
+
 	/**
 	 * Increment the download counter and the last usage date.
-	 * 
+	 *
 	 * @param re The repository entry
 	 */
 	public void incrementDownloadCounter(RepositoryEntry re);
-	
+
 	/**
 	 * Update the last usage of the specified repository entry
 	 * with a granularity of 1 minute.
-	 * 
+	 *
 	 * @param re The repository entry
 	 */
 	public void setLastUsageNowFor(RepositoryEntry re);
 
 	public Group getDefaultGroup(RepositoryEntryRef ref);
-	
+
 	/**
-	 * 
+	 *
 	 * @param identity
 	 * @param entry
 	 * @return True if the identity is member of the repository entry and its attached business groups
 	 */
 	public boolean isMember(IdentityRef identity, RepositoryEntryRef entry);
-	
+
 	public void filterMembership(IdentityRef identity, List<Long> entries);
-	
+
 	public int countMembers(RepositoryEntryRef re, String... roles);
-	
+
 	/**
 	 * Count all members (following up to business groups wainting list)
 	 * @param res
@@ -185,98 +189,98 @@ public interface RepositoryService {
 	 * @return
 	 */
 	public int countMembers(List<? extends RepositoryEntryRef> res, Identity excludeMe);
-	
+
 	/**
 	 * Return the smallest enrollment date.
-	 * 
+	 *
 	 * @param re
 	 * @param identity
 	 * @return
 	 */
 	public Date getEnrollmentDate(RepositoryEntryRef re, IdentityRef identity, String... roles);
-	
+
 	/**
 	 * Return the smallest enrollment date.
-	 * 
+	 *
 	 * @param re
 	 * @param identity
 	 * @return
 	 */
 	public Map<Long,Date> getEnrollmentDates(RepositoryEntryRef re, String... roles);
-	
+
 	/**
 	 * @param re The repository entry
 	 * @return True if the configuration allowed user to leave the entry right now
 	 */
 	public boolean isParticipantAllowedToLeave(RepositoryEntry re);
-	
+
 	/**
 	 * Return the primary keys of the authors
 	 */
 	public List<Long> getAuthors(RepositoryEntryRef re);
-	
+
 	/**
 	 * Get the members of the repository entry (the method doesn't
 	 * follow the business groups).
-	 * 
+	 *
 	 * @param re
 	 * @param roles
 	 * @return
 	 */
 	public List<Identity> getMembers(RepositoryEntryRef re, String... roles);
-	
+
 	/**
-	 * Get the 
+	 * Get the
 	 * @param re
 	 * @param followBusinessGroups
 	 * @param roles
 	 * @return
 	 */
 	public List<Identity> getMembers(List<? extends RepositoryEntryRef> re, RepositoryEntryRelationType relationType, String... roles);
-	
+
 	/**
 	 * Return all the identities the specified role linked to a repository
 	 * entry.
-	 * 
-	 * 
+	 *
+	 *
 	 * @param rolle
 	 * @return
 	 */
 	public List<Identity> getIdentitiesWithRole(String role);
-	
+
 	/**
 	 * Get the role in the specified resource, business group are included in
 	 * the query.
-	 * 
+	 *
 	 * @return The list of roles
 	 */
 	public List<String> getRoles(Identity identity, RepositoryEntryRef re);
-	
+
 	/**
 	 * Has specific role in the specified resource (doesn't follow the business groups).
-	 * 
+	 *
 	 * @return True if the specified role(s) was found.
 	 */
 	public boolean hasRole(Identity identity, RepositoryEntryRef re, String... roles);
-	
+
 	/**
 	 * Has specific role in any resource (follow or not the business groups).
-	 * 
+	 *
 	 * @return True if the specified role(s) was found.
 	 */
 	public boolean hasRole(Identity identity, boolean followBusinessGroups, String... roles);
-	
-	
+
+
 	public void addRole(Identity identity, RepositoryEntry re, String role);
-	
+
 	public void removeRole(Identity identity, RepositoryEntry re, String role);
-	
+
 	public void removeMembers(RepositoryEntry re, String... roles);
-	
+
 	public List<RepositoryEntry> searchByIdAndRefs(String id);
-	
+
 	public int countMyView(SearchMyRepositoryEntryViewParams params);
-	
+
 	/**
 	 * The identity is mandatory for the search.
 	 * @param params
@@ -285,8 +289,8 @@ public interface RepositoryService {
 	 * @return
 	 */
 	public List<RepositoryEntryMyView> searchMyView(SearchMyRepositoryEntryViewParams params, int firstResult, int maxResults);
-	
+
 	public int countAuthorView(SearchAuthorRepositoryEntryViewParams params);
-	
+
 	public List<RepositoryEntryAuthorView> searchAuthorView(SearchAuthorRepositoryEntryViewParams params, int firstResult, int maxResults);
 }
diff --git a/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java b/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java
index cac0c3838c000aa90a12f3709db0c5eac81a7228..7bcacec9f1f1a6c5ffc56760ae417f587ca738a1 100644
--- a/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java
+++ b/src/main/java/org/olat/repository/manager/RepositoryEntryDAO.java
@@ -37,17 +37,17 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 /**
- * 
+ *
  * Initial date: 26.02.2014<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
 @Service
 public class RepositoryEntryDAO {
-	
+
 	@Autowired
 	private DB dbInstance;
-	
+
 	public RepositoryEntry loadByKey(Long key) {
 		List<RepositoryEntry> entries = dbInstance.getCurrentEntityManager()
 				.createNamedQuery("loadRepositoryEntryByKey", RepositoryEntry.class)
@@ -57,9 +57,9 @@ public class RepositoryEntryDAO {
 			return null;
 		}
 		return entries.get(0);
-		
+
 	}
-	
+
 	public RepositoryEntry loadForUpdate(RepositoryEntry re) {
 		//first remove it from caches
 		dbInstance.getCurrentEntityManager().detach(re);
@@ -74,7 +74,7 @@ public class RepositoryEntryDAO {
 				.getResultList();
 		return entries == null || entries.isEmpty() ? null : entries.get(0);
 	}
-	
+
 	public RepositoryEntry loadByResourceKey(Long resourceKey) {
 		List<RepositoryEntry> entries = dbInstance.getCurrentEntityManager()
 				.createNamedQuery("loadRepositoryEntryByResourceKey", RepositoryEntry.class)
@@ -85,7 +85,7 @@ public class RepositoryEntryDAO {
 		}
 		return entries.get(0);
 	}
-	
+
 	public List<RepositoryEntry> loadByResourceKeys(Collection<Long> resourceKeys) {
 		if(resourceKeys == null || resourceKeys.isEmpty()) return Collections.emptyList();
 
@@ -95,20 +95,44 @@ public class RepositoryEntryDAO {
 		  .append(" inner join fetch v.statistics as statistics")
 		  .append(" left join fetch v.lifecycle as lifecycle")
 		  .append(" where ores.key in (:resourceKeys)");
-		
+
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("resourceKeys", resourceKeys)
 				.getResultList();
 	}
-	
+
+	public List<RepositoryEntry> loadRepositoryEntriesByExternalId(String externalId) {
+		if (externalId == null) return Collections.emptyList();
+		String query = "select v from repositoryentry as v where v.externalId=:externalId";
+
+		List<RepositoryEntry> entries = dbInstance.getCurrentEntityManager()
+				.createQuery(query, RepositoryEntry.class)
+				.setParameter("externalId", externalId)
+				.setHint("org.hibernate.cacheable", Boolean.TRUE)
+				.getResultList();
+		return entries;
+	}
+
+	public List<RepositoryEntry> loadRepositoryEntriesByExternalRef(String externalRef) {
+		if (externalRef == null) return Collections.emptyList();
+		String query = "select v from repositoryentry as v where v.externalRef=:externalRef";
+
+		List<RepositoryEntry> entries = dbInstance.getCurrentEntityManager()
+				.createQuery(query, RepositoryEntry.class)
+				.setParameter("externalRef", externalRef)
+				.setHint("org.hibernate.cacheable", Boolean.TRUE)
+				.getResultList();
+		return entries;
+	}
+
 	public List<RepositoryEntry> searchByIdAndRefs(String idAndRefs) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" as v ")
 		  .append(" inner join fetch v.olatResource as res")
 		  .append(" inner join fetch v.statistics as statistics")
 		  .append(" left join fetch v.lifecycle as lifecycle");
-		
+
 		Long id = null;
 		if(StringHelper.isLong(idAndRefs)) {
 			try {
@@ -120,7 +144,7 @@ public class RepositoryEntryDAO {
 		sb.append(" where v.externalId=:ref or v.externalRef=:ref or v.softkey=:ref");
 		if(id != null) {
 			sb.append(" or v.key=:vKey or res.resId=:vKey");
-		}	
+		}
 		TypedQuery<RepositoryEntry> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("ref", idAndRefs);
@@ -129,25 +153,25 @@ public class RepositoryEntryDAO {
 		}
 		return query.getResultList();
 	}
-	
+
 	public List<RepositoryEntry> getAllRepositoryEntries(int firstResult, int maxResults) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" as v ")
 		  .append(" inner join fetch v.olatResource as ores")
 		  .append(" inner join fetch v.statistics as statistics")
 		  .append(" left join fetch v.lifecycle as lifecycle");
-		
+
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setFirstResult(firstResult)
 				.setMaxResults(maxResults)
 				.getResultList();
 	}
-	
+
 	public OLATResource loadRepositoryEntryResource(Long key) {
 		if (key == null) return null;
 		String query = "select v.olatResource from repositoryentry as v  where v.key=:repoKey";
-		
+
 		List<OLATResource> entries = dbInstance.getCurrentEntityManager()
 				.createQuery(query, OLATResource.class)
 				.setParameter("repoKey", key)
@@ -158,7 +182,7 @@ public class RepositoryEntryDAO {
 		}
 		return entries.get(0);
 	}
-	
+
 	public OLATResource loadRepositoryEntryResourceBySoftKey(String softkey) {
 		if(softkey == null || "sf.notconfigured".equals(softkey)) {
 			return null;
@@ -174,7 +198,7 @@ public class RepositoryEntryDAO {
 		}
 		return entries.get(0);
 	}
-	
+
 	public List<RepositoryEntry> getLastUsedRepositoryEntries(String resourceTypeName, int firstResult, int maxResults) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" as v ")
@@ -183,7 +207,7 @@ public class RepositoryEntryDAO {
 		  .append(" left join fetch v.lifecycle as lifecycle")
 		  .append(" where ores.resName=:resourceTypeName")
 		  .append(" order by statistics.lastUsage desc");
-		
+
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setFirstResult(firstResult)
@@ -191,7 +215,7 @@ public class RepositoryEntryDAO {
 				.setParameter("resourceTypeName", resourceTypeName)
 				.getResultList();
 	}
-	
+
 	public List<RepositoryEntry> getRepositoryEntriesAfterTheEnd(Date date) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select v from ").append(RepositoryEntry.class.getName()).append(" as v ")
@@ -199,12 +223,12 @@ public class RepositoryEntryDAO {
 		  .append(" inner join fetch v.statistics as statistics")
 		  .append(" inner join fetch v.lifecycle as lifecycle")
 		  .append(" where lifecycle.validTo<:now");
-		
+
 		Calendar cal = Calendar.getInstance();
 		cal.setTime(date);
 		CalendarUtils.getEndOfDay(cal);
 		Date endOfDay = cal.getTime();
-		
+
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), RepositoryEntry.class)
 				.setParameter("now", endOfDay)
diff --git a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
index c5a3f7cb568c110ee0288fee1579d18441286f48..ba957b60fcec5814a3b699d6e76f30a1c0587298 100644
--- a/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
+++ b/src/main/java/org/olat/repository/manager/RepositoryServiceImpl.java
@@ -85,6 +85,7 @@ import org.olat.repository.model.SearchMyRepositoryEntryViewParams;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceManager;
 import org.olat.resource.accesscontrol.manager.ACReservationDAO;
+import org.olat.resource.accesscontrol.provider.auto.AutoAccessManager;
 import org.olat.resource.references.ReferenceManager;
 import org.olat.search.service.document.RepositoryEntryDocument;
 import org.olat.search.service.indexer.LifeFullIndexer;
@@ -93,14 +94,14 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 /**
- * 
+ *
  * Initial date: 20.02.2014<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
 @Service("repositoryService")
 public class RepositoryServiceImpl implements RepositoryService {
-	
+
 	private static final OLog log = Tracing.createLoggerFor(RepositoryServiceImpl.class);
 
 	@Autowired
@@ -114,6 +115,8 @@ public class RepositoryServiceImpl implements RepositoryService {
 	@Autowired
 	private ACReservationDAO reservationDao;
 	@Autowired
+	private AutoAccessManager autoAccessManager;
+	@Autowired
 	private ReferenceManager referenceManager;
 	@Autowired
 	private RepositoryEntryDAO repositoryEntryDAO;
@@ -148,7 +151,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 
 	@Autowired
 	private LifeFullIndexer lifeIndexer;
-	
+
 	@Override
 	public RepositoryEntry create(String initialAuthor, String resourceName,
 			String displayname, String description, OLATResource resource) {
@@ -160,11 +163,11 @@ public class RepositoryServiceImpl implements RepositoryService {
 			String resourceName, String displayname, String description, OLATResource resource, int access) {
 		return create(initialAuthorAlt, initialAuthor, resourceName, displayname, description, resource, access);
 	}
-	
+
 	private RepositoryEntry create(String initialAuthorName, Identity initialAuthor, String resourceName,
-			String displayname, String description, OLATResource resource, int access) { 
+			String displayname, String description, OLATResource resource, int access) {
 		Date now = new Date();
-		
+
 		RepositoryEntry re = new RepositoryEntry();
 		if(StringHelper.containsNonWhitespace(initialAuthorName)) {
 			re.setInitialAuthor(initialAuthorName);
@@ -191,7 +194,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 			dbInstance.getCurrentEntityManager().persist(resource);
 		}
 		re.setOlatResource(resource);
-		
+
 		RepositoryEntryStatistics statistics = new RepositoryEntryStatistics();
 		statistics.setLastUsage(now);
 		statistics.setCreationDate(now);
@@ -201,9 +204,9 @@ public class RepositoryServiceImpl implements RepositoryService {
 		statistics.setNumOfRatings(0l);
 		statistics.setNumOfComments(0l);
 		dbInstance.getCurrentEntityManager().persist(statistics);
-		
+
 		re.setStatistics(statistics);
-		
+
 		Group group = groupDao.createGroup();
 		RepositoryEntryToGroupRelation rel = new RepositoryEntryToGroupRelation();
 		rel.setCreationDate(new Date());
@@ -214,13 +217,15 @@ public class RepositoryServiceImpl implements RepositoryService {
 		Set<RepositoryEntryToGroupRelation> rels = new HashSet<>(2);
 		rels.add(rel);
 		re.setGroups(rels);
-		
+
 		if(initialAuthor != null) {
 			groupDao.addMembershipTwoWay(group, initialAuthor, GroupRoles.owner.name());
 		}
-		
+
 		dbInstance.getCurrentEntityManager().persist(re);
-		return re;	
+
+		autoAccessManager.grantAccess(re);
+		return re;
 	}
 
 	@Override
@@ -229,7 +234,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 		OLATResource copyResource = resourceManager.createOLATResourceInstance(sourceResource.getResourceableTypeName());
 		RepositoryEntry copyEntry = create(author, null, sourceEntry.getResourcename(), displayname,
 				sourceEntry.getDescription(), copyResource, RepositoryEntry.ACC_OWNERS);
-		
+
 		//copy all fields
 		copyEntry.setAuthors(sourceEntry.getAuthors());
 		copyEntry.setCredits(sourceEntry.getCredits());
@@ -238,13 +243,13 @@ public class RepositoryServiceImpl implements RepositoryService {
 		copyEntry.setObjectives(sourceEntry.getObjectives());
 		copyEntry.setRequirements(sourceEntry.getRequirements());
 		copyEntry = dbInstance.getCurrentEntityManager().merge(copyEntry);
-	
+
 		RepositoryHandler handler = RepositoryHandlerFactory.getInstance().getRepositoryHandler(sourceEntry);
 		copyEntry = handler.copy(author, sourceEntry, copyEntry);
 
 		//copy the image
 		RepositoryManager.getInstance().copyImage(sourceEntry, copyEntry);
-		
+
 		//copy media container
 		VFSContainer sourceMediaContainer = handler.getMediaContainer(sourceEntry);
 		if(sourceMediaContainer != null) {
@@ -254,8 +259,8 @@ public class RepositoryServiceImpl implements RepositoryService {
 
 		ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_CREATE, getClass(),
 				LoggingResourceable.wrap(copyEntry, OlatResourceableType.genRepoEntry));
-		
-		
+
+
 		lifeIndexer.indexDocument(RepositoryEntryDocument.TYPE, copyEntry.getKey());
 		return copyEntry;
 	}
@@ -266,9 +271,10 @@ public class RepositoryServiceImpl implements RepositoryService {
 		RepositoryEntry mergedRe = dbInstance.getCurrentEntityManager().merge(re);
 		dbInstance.commit();
 		lifeIndexer.indexDocument(RepositoryEntryDocument.TYPE, mergedRe.getKey());
+		autoAccessManager.grantAccess(re);
 		return mergedRe;
 	}
-	
+
 	@Override
 	public RepositoryEntry loadByKey(Long key) {
 		return repositoryEntryDAO.loadByKey(key);
@@ -278,7 +284,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 	public RepositoryEntry loadByResourceKey(Long resourceKey) {
 		return repositoryEntryDAO.loadByResourceKey(resourceKey);
 	}
-	
+
 	@Override
 	public List<RepositoryEntry> loadByResourceKeys(Collection<Long> resourceKeys) {
 		return repositoryEntryDAO.loadByResourceKeys(resourceKeys);
@@ -294,6 +300,16 @@ public class RepositoryServiceImpl implements RepositoryService {
 		return repositoryEntryDAO.loadRepositoryEntryResourceBySoftKey(softkey);
 	}
 
+	@Override
+	public List<RepositoryEntry> loadRepositoryEntriesByExternalId(String externalId) {
+		return repositoryEntryDAO.loadRepositoryEntriesByExternalId(externalId);
+	}
+
+	@Override
+	public List<RepositoryEntry> loadRepositoryEntriesByExternalRef(String externalRef) {
+		return repositoryEntryDAO.loadRepositoryEntriesByExternalRef(externalRef);
+	}
+
 	@Override
 	public VFSLeaf getIntroductionImage(RepositoryEntry re) {
 		VFSContainer repositoryHome = new LocalFolderImpl(new File(FolderConfig.getCanonicalRepositoryHome()));
@@ -320,13 +336,13 @@ public class RepositoryServiceImpl implements RepositoryService {
 				if(item instanceof VFSLeaf
 						&& item.getName().startsWith(re.getKey().toString())
 						&& (item.getName().endsWith(".mp4") || item.getName().endsWith(".m4v") || item.getName().endsWith(".flv")) ) {
-					return (VFSLeaf)item;	
-				}	
+					return (VFSLeaf)item;
+				}
 			}
 		}
 		return null;
 	}
-	
+
 	@Override
 	public RepositoryEntry deleteSoftly(RepositoryEntry re, Identity deletedBy, boolean owners) {
 		RepositoryEntry reloadedRe = repositoryEntryDAO.loadForUpdate(re);
@@ -370,7 +386,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 	@Override
 	public ErrorList deletePermanently(RepositoryEntry entry, Identity identity, Roles roles, Locale locale) {
 		ErrorList errors = new ErrorList();
-		
+
 		boolean debug = log.isDebug();
 
 		// invoke handler delete callback
@@ -386,7 +402,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 
 		userCourseInformationsManager.deleteUserCourseInformations(entry);
 		certificatesManager.deleteRepositoryEntry(entry);
-		
+
 		// delete all bookmarks referencing deleted entry
 		CoreSpringFactory.getImpl(MarkManager.class).deleteMarks(entry);
 		// delete all catalog entries referencing deleted entry
@@ -407,7 +423,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 		//delete lectures
 		CoreSpringFactory.getImpl(LectureService.class).delete(entry);
 		dbInstance.commit();
-		
+
 		// inform handler to do any cleanup work... handler must delete the
 		// referenced resourceable a swell.
 		handler.cleanupOnDelete(entry, resource);
@@ -426,9 +442,9 @@ public class RepositoryServiceImpl implements RepositoryService {
 		if(debug) log.debug("deleteRepositoryEntry Done");
 		return errors;
 	}
-	
+
 	/**
-	 * 
+	 *
 	 * @param entry
 	 */
 	@Override
@@ -451,12 +467,12 @@ public class RepositoryServiceImpl implements RepositoryService {
 			groupDao.removeGroup(defaultGroup);
 		}
 		dbInstance.commit();
-		
+
 		OLATResource reloadedResource = resourceManager.findResourceById(resourceKey);
 		if(reloadedResource != null) {
 			dbInstance.getCurrentEntityManager().remove(reloadedResource);
 		}
-		
+
 		dbInstance.commit();
 	}
 
@@ -522,7 +538,7 @@ public class RepositoryServiceImpl implements RepositoryService {
 	/**
 	 * Get the role in the specified resource, business group are included in
 	 * the query.
-	 * 
+	 *
 	 */
 	@Override
 	public List<String> getRoles(Identity identity, RepositoryEntryRef re) {
@@ -587,12 +603,12 @@ public class RepositoryServiceImpl implements RepositoryService {
 		return reToGroupDao.countMembers(re, roles);
 	}
 
-	
+
 	@Override
 	public int countMembers(List<? extends RepositoryEntryRef> res, Identity excludeMe) {
 		return reToGroupDao.countMembers(res, excludeMe);
 	}
-	
+
 	@Override
 	public Date getEnrollmentDate(RepositoryEntryRef re, IdentityRef identity, String... roles) {
 		return reToGroupDao.getEnrollmentDate(re, identity, roles);
diff --git a/src/main/java/org/olat/resource/accesscontrol/ACService.java b/src/main/java/org/olat/resource/accesscontrol/ACService.java
index 919f464cd2ae0ce1c08b28e938b33f575e0269d2..c5aecfe996835c429d100463b6243440307f1994 100644
--- a/src/main/java/org/olat/resource/accesscontrol/ACService.java
+++ b/src/main/java/org/olat/resource/accesscontrol/ACService.java
@@ -38,19 +38,19 @@ import org.olat.resource.accesscontrol.ui.OrderTableItem;
 import org.olat.user.propertyhandlers.UserPropertyHandler;
 
 /**
- * 
+ *
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 public interface ACService {
-	
+
 	/**
-	 * 
+	 *
 	 * @param resource
-	 * @param atDate 
+	 * @param atDate
 	 * @return
 	 */
 	public boolean isResourceAccessControled(OLATResource resource, Date atDate);
-	
+
 	/**
 	 * The rule to access a business group:<br/>
 	 * -No offer, access is free<br/>
@@ -62,11 +62,11 @@ public interface ACService {
 	 * @return
 	 */
 	public AccessResult isAccessible(BusinessGroup group, Identity forId, boolean allowNonInteractiveAccess);
-	
+
 	public AccessResult isAccessible(RepositoryEntry entry, Identity forId, boolean allowNonInteractiveAccess);
-	
+
 	/**
-	 * 
+	 *
 	 * @param entry
 	 * @param forId
 	 * @param knowMember If you know that the forId is a member
@@ -75,24 +75,24 @@ public interface ACService {
 	 */
 	public AccessResult isAccessible(RepositoryEntry entry, Identity forId, Boolean knowMember, boolean allowNonInteractiveAccess);
 
-	
+
 	public Offer createOffer(OLATResource resource, String resourceName);
-	
+
 	public Offer save(Offer offer);
-	
+
 	public void deleteOffer(Offer offer);
-	
-	
+
+
 	public List<OLATResourceAccess> filterRepositoryEntriesWithAC(List<RepositoryEntry> repoEntries);
-	
+
 	public List<OLATResourceAccess> filterResourceWithAC(List<OLATResource> resources);
-	
+
 	public Set<Long> filterResourcesWithAC(Collection<Long> resourceKeys);
-	
+
 	public List<Offer> findOfferByResource(OLATResource resource, boolean valid, Date atDate);
-	
+
 	/**
-	 * 
+	 *
 	 * @param resourceKeys This parameter is mandatory and must not be empty
 	 * @param resourceType
 	 * @param valid
@@ -103,22 +103,26 @@ public interface ACService {
 
 	/**
 	 * Get the list of access methods for a business group that are currently available
-	 * @param group 
-	 * @param valid 
+	 * @param group
+	 * @param valid
 	 * @param atDate
 	 * @return The list of OfferAccess objects that represent available access methods
 	 */
 	public List<OfferAccess> getAccessMethodForBusinessGroup(BusinessGroup group, boolean valid, Date atDate);
-	
+
+	public List<OfferAccess> getValidOfferAccess(OLATResource resource, AccessMethod method);
+
+	public List<AccessMethod> getAvailableMethodsByType(Class<? extends AccessMethod> type);
+
 	public OfferAccess saveOfferAccess(OfferAccess link);
-	
+
 	public AccessResult accessResource(Identity identity, OfferAccess link, Object argument);
 
 	public boolean allowAccesToResource(Identity identity, Offer offer);
-	
+
 	public boolean denyAccesToResource(Identity identity, Offer offer);
-	
-	
+
+
 	/**
 	 * Get the reservation form an identity on a resource
 	 * @param identity
@@ -126,21 +130,21 @@ public interface ACService {
 	 * @return
 	 */
 	public ResourceReservation getReservation(Identity identity, OLATResource resource);
-	
+
 	/**
 	 * Get the reservations pending a list of resources.
 	 * @param resources
 	 * @return
 	 */
 	public List<ResourceReservation> getReservations(List<OLATResource> resources);
-	
+
 	/**
 	 * The list of pending reservations
 	 * @param identity
 	 * @return
 	 */
 	public List<ResourceReservation> getReservations(Identity identity);
-	
+
 	/**
 	 * Reserve a resource
 	 * @param identity
@@ -148,57 +152,59 @@ public interface ACService {
 	 * @return
 	 */
 	public boolean reserveAccessToResource(Identity identity, OfferAccess offer);
-	
+
 	/**
 	 * A user must sometimes explicitly accept a reservation.
 	 * @param identity
 	 * @param reservation
 	 */
 	public void acceptReservationToResource(Identity identity, ResourceReservation reservation);
-	
+
 	/**
 	 * Cancel a reservation
 	 * @param identity
 	 * @param reservation
 	 */
 	public void removeReservation(Identity ureqIdentity, Identity identity, ResourceReservation reservation);
-	
+
 	public int countReservations(OLATResource resource);
-	
+
 	public void cleanupReservations();
-	
+
 	/**
-	 * 
+	 *
 	 * @param resources
 	 * @return
 	 */
 	public List<ACResourceInfo> getResourceInfos(List<OLATResource> resources);
 
 	public String resolveDisplayName(OLATResource resource);
-	
+
 	public void enableMethod(Class<? extends AccessMethod> type, boolean enable);
-	
+
 	public List<AccessMethod> getAvailableMethods(Identity identity, Roles roles);
-	
+
 	public OfferAccess createOfferAccess(Offer offer, AccessMethod method);
-	
+
 	public void deletedLinkToMethod(OfferAccess link);
-	
+
 	public List<OfferAccess> getOfferAccess(Offer offer, boolean valid);
-	
+
 	public Order loadOrderByKey(Long key);
-	
+
 	public List<Order> findOrders(Identity delivery, OrderStatus... status);
-	
+
 	public List<AccessTransaction> findAccessTransactions(Order order);
-	
+
 	public List<Order> findOrders(OLATResource resource, OrderStatus... status);
-	
+
+	public List<Order> findOrder(OLATResource resource, Identity identity, AccessMethod method);
+
 	public int countOrderItems(OLATResource resource, IdentityRef delivery, Long orderNr, Date from, Date to,
 			OrderStatus[] statuss);
-	
+
 	public List<OrderTableItem> findOrderItems(OLATResource resource, IdentityRef delivery, Long orderNr, Date from, Date to,
 			OrderStatus[] status, int firstResult, int maxResults,
 			List<UserPropertyHandler> userPropertyHandlers, SortKey... orderBy);
-	
+
 }
diff --git a/src/main/java/org/olat/resource/accesscontrol/AccessControlModule.java b/src/main/java/org/olat/resource/accesscontrol/AccessControlModule.java
index b528177653f4bb2965040ab21c849715ba86d9fc..8dfb009d4797ca8cd63bda42353ab46ad153cc62 100644
--- a/src/main/java/org/olat/resource/accesscontrol/AccessControlModule.java
+++ b/src/main/java/org/olat/resource/accesscontrol/AccessControlModule.java
@@ -40,28 +40,29 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 /**
- * 
+ *
  * Description:<br>
  * Module for access control of OLAT Resource
- * 
+ *
  * <P>
  * Initial Date:  14 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Service("acModule")
 public class AccessControlModule extends AbstractSpringModule implements ConfigOnOff {
-	
+
 	private static final OLog log = Tracing.createLoggerFor(AccessControlModule.class);
-	
+
 	public static final String AC_ENABLED = "resource.accesscontrol.enabled";
 	public static final String AC_HOME_ENABLED = "resource.accesscontrol.home.overview";
 	private static final String VAT_ENABLED = "vat.enabled";
 	private static final String VAT_RATE = "vat.rate";
 	private static final String VAT_NR = "vat.number";
-	
+
 	private static final String TOKEN_ENABLED = "method.token.enabled";
 	private static final String FREE_ENABLED = "method.free.enabled";
 	private static final String PAYPAL_ENABLED = "method.paypal.enabled";
+	private static final String AUTO_ENABLED = "method.auto.enabled";
 
 	@Value("${resource.accesscontrol.enabled:true}")
 	private boolean enabled;
@@ -69,6 +70,8 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 	private boolean homeOverviewEnabled;
 	@Value("${method.free.enabled:true}")
 	private boolean freeEnabled;
+	@Value("${method.auto.enabled:false}")
+	private boolean autoEnabled;
 	@Value("${method.token.enabled:true}")
 	private boolean tokenEnabled;
 	@Value("${method.paypal.enabled:false}")
@@ -97,7 +100,7 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 		if(StringHelper.containsNonWhitespace(enabledObj)) {
 			enabled = "true".equals(enabledObj);
 		}
-		
+
 		String tokenEnabledObj = getStringPropertyValue(TOKEN_ENABLED, true);
 		if(StringHelper.containsNonWhitespace(tokenEnabledObj)) {
 			tokenEnabled = "true".equals(tokenEnabledObj);
@@ -107,22 +110,27 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 		if(StringHelper.containsNonWhitespace(paypalEnabledObj)) {
 			paypalEnabled = "true".equals(paypalEnabledObj);
 		}
-		
+
 		String freeEnabledObj = getStringPropertyValue(FREE_ENABLED, true);
 		if(StringHelper.containsNonWhitespace(freeEnabledObj)) {
 			freeEnabled = "true".equals(freeEnabledObj);
 		}
-		
+
+		String autoEnabledObj = getStringPropertyValue(AUTO_ENABLED, true);
+		if(StringHelper.containsNonWhitespace(autoEnabledObj)) {
+			autoEnabled = "true".equals(autoEnabledObj);
+		}
+
 		String homeEnabledObj = getStringPropertyValue(AC_HOME_ENABLED, true);
 		if(StringHelper.containsNonWhitespace(homeEnabledObj)) {
 			homeOverviewEnabled = "true".equals(homeEnabledObj);
 		}
-		
+
 		String vatEnabledObj = getStringPropertyValue(VAT_ENABLED, true);
 		if(StringHelper.containsNonWhitespace(vatEnabledObj)) {
 			vatEnabled = "true".equals(vatEnabledObj);
 		}
-		
+
 		String vatRateObj = getStringPropertyValue(VAT_RATE, true);
 		if(StringHelper.containsNonWhitespace(vatRateObj)) {
 			try {
@@ -131,7 +139,7 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 				log.error("Error parsing the VAT: " + vatRateObj, e);
 			}
 		}
-		
+
 		String vatNrObj = getStringPropertyValue(VAT_NR, true);
 		if(StringHelper.containsNonWhitespace(vatNrObj)) {
 			vatNumber = vatNrObj;
@@ -143,12 +151,12 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 	protected void initFromChangedProperties() {
 		init();
 	}
-	
+
 	@Override
 	public boolean isEnabled() {
 		return enabled;
 	}
-	
+
 	public void setEnabled(boolean enabled) {
 		if(this.enabled != enabled) {
 			setStringProperty(AC_ENABLED, Boolean.toString(enabled), true);
@@ -180,7 +188,20 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 			acMethodManager.enableMethod(FreeAccessMethod.class, freeEnabled);
 		}
 	}
-	
+
+	public boolean isAutoEnabled() {
+		return autoEnabled;
+	}
+
+	public void setAutoEnabled(boolean autoEnabled) {
+		if(this.autoEnabled != autoEnabled) {
+			setStringProperty(AUTO_ENABLED, Boolean.toString(autoEnabled), true);
+		}
+		if(acMethodManager != null) {
+			acMethodManager.enableAutoMethods(autoEnabled);
+		}
+	}
+
 	public boolean isPaypalEnabled() {
 		return paypalEnabled;
 	}
@@ -203,7 +224,7 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 			setStringProperty(AC_HOME_ENABLED, Boolean.toString(homeOverviewEnabled), true);
 		}
 	}
-	
+
 	public boolean isVatEnabled() {
 		return vatEnabled;
 	}
@@ -231,21 +252,21 @@ public class AccessControlModule extends AbstractSpringModule implements ConfigO
 	}
 
 	public List<AccessMethodHandler> getMethodHandlers() {
-		return new ArrayList<AccessMethodHandler>(methodHandlers);
+		return new ArrayList<>(methodHandlers);
 	}
-	
+
 	public void setMethodHandlers(List<AccessMethodHandler> handlers) {
 		if(handlers != null) {
 			methodHandlers.addAll(handlers);
 		}
 	}
-	
+
 	public void addMethodHandler(AccessMethodHandler handler) {
 		if(handler != null) {
 			methodHandlers.add(handler);
 		}
 	}
-	
+
 	public void removeMethodHandler(AccessMethodHandler handler) {
 		if(handler != null) {
 			methodHandlers.remove(handler);
diff --git a/src/main/java/org/olat/resource/accesscontrol/_spring/acContext.xml b/src/main/java/org/olat/resource/accesscontrol/_spring/acContext.xml
index 6b353d7993678bd38141156aa4722cf8cb49068b..fa673d4cfcf31588b0e9f056399f042a9fb1bee7 100644
--- a/src/main/java/org/olat/resource/accesscontrol/_spring/acContext.xml
+++ b/src/main/java/org/olat/resource/accesscontrol/_spring/acContext.xml
@@ -2,9 +2,9 @@
 <beans xmlns="http://www.springframework.org/schema/beans"
 	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 	xmlns:context="http://www.springframework.org/schema/context"
-	xsi:schemaLocation="http://www.springframework.org/schema/beans 
+	xsi:schemaLocation="http://www.springframework.org/schema/beans
                         http://www.springframework.org/schema/beans/spring-beans.xsd
-                        http://www.springframework.org/schema/context 
+                        http://www.springframework.org/schema/context
                         http://www.springframework.org/schema/context/spring-context.xsd">
 
 	<context:component-scan base-package="org.olat.resource.accesscontrol" />
@@ -12,29 +12,29 @@
 	<!--  Extension for archiver -->
 	<bean class="org.olat.resource.accesscontrol.ui.OrdersActionExtension" init-method="initExtensionPoints">
 		<constructor-arg index="0" ref="acModule" />
-		<property name="actionController">	
+		<property name="actionController">
 			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
 				<property name="className" value="org.olat.resource.accesscontrol.ui.OrdersAdminController"/>
 			</bean>
 		</property>
-		
+
 		<property name="i18nActionKey" value="order.menu.title.alt"/>
 		<property name="i18nDescriptionKey" value="order.menu.title.alt"/>
 		<property name="translationPackage" value="org.olat.resource.accesscontrol.ui"/>
 		<property name="extensionPoints">
-			<list>	
-				<value>org.olat.course.archiver.ArchiverMainController</value>		
+			<list>
+				<value>org.olat.course.archiver.ArchiverMainController</value>
 			</list>
 		</property>
 		<property name="order" value="267" />
 	</bean>
-	
+
 	<!--  The "orders"  (Buchungen) in Home -->
 	<bean class="org.olat.resource.accesscontrol.ui.HomeOrdersActionExtension" name="accesscontrol.actExt" init-method="initExtensionPoints">
 		<constructor-arg index="0" ref="acModule" />
 		<property name="order" value="109" />
 		<property name="enabled" value="${minimalhome.ext.bookings}"></property>
-		<property name="actionController">	
+		<property name="actionController">
 			<bean class=" org.olat.core.gui.control.creator.FactoryControllerCreator" scope="prototype">
 				<property name="factoryName" value="org.olat.resource.accesscontrol.ACUIFactory"/>
 				<property name="factoryMethod" value="createOrdersController"/>
@@ -47,61 +47,61 @@
 		<property name="i18nActionKey" value="menu.orders"/>
 		<property name="i18nDescriptionKey" value="menu.orders.alt"/>
 		<property name="extensionPoints">
-			<list>	
+			<list>
 				<value>org.olat.home.HomeMainController</value>
 			</list>
 		</property>
 	</bean>
-	
+
 	<!-- Access control administration -->
 	<bean class="org.olat.core.extensions.action.GenericActionExtension" id="sysadmin.menupoint.access" init-method="initExtensionPoints">
-		<property name="actionController">	
+		<property name="actionController">
 			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
 				<property name="className" value="org.olat.resource.accesscontrol.ui.AccessControlAdminController"/>
 			</bean>
 		</property>
 		<property name="navigationKey" value="accesscontrol" />
-		<property name="parentTreeNodeIdentifier" value="sysconfigParent" /> 
+		<property name="parentTreeNodeIdentifier" value="sysconfigParent" />
 		<property name="i18nActionKey" value="admin.menu.title"/>
 		<property name="i18nDescriptionKey" value="admin.menu.title.alt"/>
 		<property name="translationPackage" value="org.olat.resource.accesscontrol.ui"/>
 		<property name="extensionPoints">
-			<list>	
-				<value>org.olat.admin.SystemAdminMainController</value>		
+			<list>
+				<value>org.olat.admin.SystemAdminMainController</value>
 			</list>
 		</property>
 		<property name="order" value="7220" />
 	</bean>
-	
+
 	<bean id="acReservationCleanupJob" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
     	<property name="jobDetail" ref="acReservationCleanupJobDetail"/>
     	<!-- 60 seconds -->
   		<property name="repeatInterval" value="60000"/>
     	<property name="startDelay" value="30000" />
 	</bean>
-	
+
 	<bean id="acReservationCleanupJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
 		<property name="jobClass" value="org.olat.resource.accesscontrol.manager.ReservationsJob" />
 	</bean>
-	
+
 	<bean id="freeAccessHandler" class="org.olat.resource.accesscontrol.provider.free.FreeAccessHandler"/>
 	<bean id="tokenAccessHandler" class="org.olat.resource.accesscontrol.provider.token.TokenAccessHandler"/>
-	
+
 	<!--  Orders admin panel -->
 	<bean class="org.olat.core.extensions.action.GenericActionExtension" init-method="initExtensionPoints">
-		<property name="actionController">	
+		<property name="actionController">
 			<bean class="org.olat.core.gui.control.creator.AutoCreator" scope="prototype">
 				<property name="className" value="org.olat.resource.accesscontrol.ui.OrdersAdminController"/>
 			</bean>
 		</property>
 		<property name="navigationKey" value="booking" />
-		<property name="parentTreeNodeIdentifier" value="modulesParent" /> 
+		<property name="parentTreeNodeIdentifier" value="modulesParent" />
 		<property name="i18nActionKey" value="order.menu.title"/>
 		<property name="i18nDescriptionKey" value="order.menu.title.alt"/>
 		<property name="translationPackage" value="org.olat.resource.accesscontrol.ui"/>
 		<property name="extensionPoints">
-			<list>	
-				<value>org.olat.admin.SystemAdminMainController</value>		
+			<list>
+				<value>org.olat.admin.SystemAdminMainController</value>
 			</list>
 		</property>
 		<property name="order" value="7600" />
diff --git a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
index 25fb462c3e903c58ff8e1385405fad891a4b95a6..a517ed0173b7f314ef63370b40f63b68eefa950e 100644
--- a/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
+++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACFrontendManager.java
@@ -79,19 +79,19 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 /**
- * 
+ *
  * Description:<br>
  * The access control is not intend for security check.
- * 
+ *
  * <P>
  * Initial Date:  14 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Service("acService")
 public class ACFrontendManager implements ACService {
-	
+
 	private static final OLog log = Tracing.createLoggerFor(ACFrontendManager.class);
-	
+
 	@Autowired
 	private DB dbInstance;
 	@Autowired
@@ -135,7 +135,7 @@ public class ACFrontendManager implements ACService {
 		if(!accessModule.isEnabled()) {
 			return new AccessResult(true);
 		}
-		
+
 		boolean member;
 		if(knowMember == null) {
 			member = repositoryService.isMember(forId, entry);
@@ -145,7 +145,7 @@ public class ACFrontendManager implements ACService {
 		if(member) {
 			return new AccessResult(true);
 		}
-		
+
 		Date now = dateNow();
 		List<Offer> offers = accessManager.findOfferByResource(entry.getOlatResource(), true, now);
 		if(offers.isEmpty()) {
@@ -154,32 +154,32 @@ public class ACFrontendManager implements ACService {
 				return new AccessResult(false);
 			} else {
 				return new AccessResult(true);
-			}	
+			}
 		}
 		return isAccessible(forId, offers, allowNonInteractiveAccess);
 	}
-	
+
 	@Override
 	public AccessResult isAccessible(RepositoryEntry entry, Identity forId, boolean allowNonInteractiveAccess) {
 		if(!accessModule.isEnabled()) {
 			return new AccessResult(true);
 		}
-		
+
 		boolean member = repositoryService.isMember(forId, entry);
 		return isAccessible(entry, forId, new Boolean(member), allowNonInteractiveAccess);
 	}
-	
+
 	/**
-	 * 
+	 *
 	 * @param resource
-	 * @param atDate 
+	 * @param atDate
 	 * @return
 	 */
 	@Override
 	public boolean isResourceAccessControled(OLATResource resource, Date atDate) {
 		return methodManager.isValidMethodAvailable(resource, atDate);
 	}
-	
+
 	/**
 	 * The rule to access a business group:<br/>
 	 * -No offer, access is free<br/>
@@ -203,7 +203,7 @@ public class ACFrontendManager implements ACService {
 		if(roles.contains(GroupRoles.participant.name())) {
 			return new AccessResult(true);
 		}
-		
+
 		Date now = dateNow();
 		OLATResource resource = OLATResourceManager.getInstance().findResourceable(group);
 		List<Offer> offers = accessManager.findOfferByResource(resource, true, now);
@@ -213,12 +213,12 @@ public class ACFrontendManager implements ACService {
 				return new AccessResult(false);
 			} else {
 				return new AccessResult(true);
-			}	
+			}
 		}
-		
+
 		return isAccessible(forId, offers, allowNonInteractiveAccess);
 	}
-	
+
 	protected AccessResult isAccessible(Identity identity, List<Offer> offers, boolean allowNonInteractiveAccess) {
 		List<OfferAccess> offerAccess = methodManager.getOfferAccess(offers, true);
 		if(offerAccess.isEmpty()) {
@@ -256,7 +256,7 @@ public class ACFrontendManager implements ACService {
 			resourceKeys.add(ores.getKey());
 			resourceTypes.add(ores.getResourceableTypeName());
 		}
-		
+
 		String resourceType = null;
 		if(resourceTypes.size() == 1) {
 			resourceType = resourceTypes.iterator().next();
@@ -276,7 +276,7 @@ public class ACFrontendManager implements ACService {
 			resourceKeys.add(resource.getKey());
 			resourceTypes.add(resource.getResourceableTypeName());
 		}
-		
+
 		String resourceType = null;
 		if(resourceTypes.size() == 1) {
 			resourceType = resourceTypes.iterator().next();
@@ -294,9 +294,9 @@ public class ACFrontendManager implements ACService {
 	public List<Offer> findOfferByResource(OLATResource resource, boolean valid, Date atDate) {
 		return accessManager.findOfferByResource(resource, valid, atDate);
 	}
-	
+
 	/**
-	 * 
+	 *
 	 * @param resourceKeys This parameter is mandatory and must not be empty!
 	 */
 	@Override
@@ -309,8 +309,8 @@ public class ACFrontendManager implements ACService {
 
 	/**
 	 * Get the list of access methods for a business group that are currently available
-	 * @param group 
-	 * @param valid 
+	 * @param group
+	 * @param valid
 	 * @param atDate
 	 * @return The list of OfferAccess objects that represent available access methods
 	 */
@@ -327,6 +327,16 @@ public class ACFrontendManager implements ACService {
 		return offerAccess;
 	}
 
+	@Override
+	public List<OfferAccess> getValidOfferAccess(OLATResource resource, AccessMethod method) {
+		return methodManager.getValidOfferAccess(resource, method);
+	}
+
+	@Override
+	public List<AccessMethod> getAvailableMethodsByType(Class<? extends AccessMethod> type) {
+		return methodManager.getAvailableMethodsByType(type);
+	}
+
 	@Override
 	public Offer save(Offer offer) {
 		return accessManager.saveOffer(offer);
@@ -340,20 +350,20 @@ public class ACFrontendManager implements ACService {
 		}
 		return methodManager.save(link);
 	}
-	
+
 	@Override
 	public AccessResult accessResource(Identity identity, OfferAccess link, Object argument) {
 		if(link == null || link.getOffer() == null || link.getMethod() == null) {
 			log.audit("Access refused (no offer) to: " + link + " for " + identity);
 			return new AccessResult(false);
 		}
-		
+
 		AccessMethodHandler handler = accessModule.getAccessMethodHandler(link.getMethod().getType());
 		if(handler == null) {
 			log.audit("Access refused (no handler method) to: " + link + " for " + identity);
 			return new AccessResult(false);
 		}
-		
+
 		if(handler.checkArgument(link, argument)) {
 			if(allowAccesToResource(identity, link.getOffer())) {
 				Order order = orderManager.saveOneClick(identity, link);
@@ -370,7 +380,7 @@ public class ACFrontendManager implements ACService {
 		}
 		return new AccessResult(false);
 	}
-	
+
 	@Override
 	public void acceptReservationToResource(Identity identity, ResourceReservation reservation) {
 		OLATResource resource = reservation.getResource();
@@ -396,7 +406,7 @@ public class ACFrontendManager implements ACService {
 	public ResourceReservation getReservation(Identity identity, OLATResource resource) {
 		return reservationDao.loadReservation(identity, resource);
 	}
-	
+
 	@Override
 	public List<ResourceReservation> getReservations(List<OLATResource> resources) {
 		return reservationDao.loadReservations(resources);
@@ -427,7 +437,7 @@ public class ACFrontendManager implements ACService {
 				if(reservation != null) {
 					reserved = true;
 				}
-				
+
 				int currentCount = businessGroupService.countMembers(reloadedGroup, GroupRoles.participant.name());
 				int reservations = reservationDao.countReservations(resource);
 				if(currentCount + reservations < reloadedGroup.getMaxParticipants().intValue()) {
@@ -458,13 +468,13 @@ public class ACFrontendManager implements ACService {
 		if(offer.getKey() == null) {
 			return false;
 		}
-		
+
 		//check the resource
 		OLATResource resource = offer.getResource();
 		if(resource == null || resource.getKey() == null || resource.getResourceableId() == null || resource.getResourceableTypeName() == null) {
 			return false;
 		}
-		
+
 		String resourceType = resource.getResourceableTypeName();
 		if("BusinessGroup".equals(resourceType)) {
 			BusinessGroup group = businessGroupService.loadBusinessGroup(resource);
@@ -490,13 +500,13 @@ public class ACFrontendManager implements ACService {
 		if(offer.getKey() == null) {
 			return false;
 		}
-		
+
 		//check the resource
 		OLATResource resource = offer.getResource();
 		if(resource == null || resource.getKey() == null || resource.getResourceableId() == null || resource.getResourceableTypeName() == null) {
 			return false;
 		}
-		
+
 		String resourceType = resource.getResourceableTypeName();
 		if("BusinessGroup".equals(resourceType)) {
 			BusinessGroup group = businessGroupService.loadBusinessGroup(resource);
@@ -534,13 +544,13 @@ public class ACFrontendManager implements ACService {
 		}
 		return null;
 	}
-	
+
 	@Override
 	public List<ACResourceInfo> getResourceInfos(List<OLATResource> resources) {
 		if(resources == null || resources.isEmpty()) {
 			return Collections.emptyList();
 		}
-		
+
 		List<OLATResource> groupResources = new ArrayList<OLATResource>(resources.size());
 		List<OLATResource> repositoryResources = new ArrayList<OLATResource>(resources.size());
 		for(OLATResource resource:resources) {
@@ -611,7 +621,12 @@ public class ACFrontendManager implements ACService {
 	public List<Order> findOrders(Identity delivery, OrderStatus... status) {
 		return orderManager.findOrdersByDelivery(delivery, status);
 	}
-	
+
+	@Override
+	public List<Order> findOrder(OLATResource resource, Identity identity, AccessMethod method) {
+		return orderManager.findOrdersByResource(resource, identity, method);
+	}
+
 	@Override
 	public List<AccessTransaction> findAccessTransactions(Order order) {
 		return transactionManager.loadTransactionsForOrder(order);
@@ -641,7 +656,7 @@ public class ACFrontendManager implements ACService {
 		for(AccessMethod method:methods) {
 			methodMap.put(method.getKey().toString(), method);
 		}
-		
+
 		List<RawOrderItem> rawOrders = orderManager.findNativeOrderItems(resource, delivery, orderNr, from, to, status,
 				firstResult, maxResults, userPropertyHandlers, orderBy);
 		List<OrderTableItem> items = new ArrayList<>(rawOrders.size());
@@ -649,7 +664,7 @@ public class ACFrontendManager implements ACService {
 			String orderStatusStr = rawOrder.getOrderStatus();
 			OrderStatus orderStatus = OrderStatus.valueOf(orderStatusStr);
 			Status finalStatus = getStatus(orderStatusStr,  rawOrder.getTrxStatus(), rawOrder.getPspTrxStatus());
-			
+
 			String methodIds = rawOrder.getTrxMethodIds();
 			List<AccessMethod> orderMethods = new ArrayList<>(2);
 			if(StringHelper.containsNonWhitespace(methodIds)) {
@@ -660,23 +675,23 @@ public class ACFrontendManager implements ACService {
 					}
 				}
 			}
-			
+
 			OrderTableItem item = new OrderTableItem(rawOrder.getOrderKey(), rawOrder.getOrderNr(),
 					rawOrder.getTotal(), rawOrder.getCreationDate(), orderStatus, finalStatus,
 					rawOrder.getDeliveryKey(), rawOrder.getUsername(), rawOrder.getUserProperties(), orderMethods);
 			item.setResourceDisplayname(rawOrder.getResourceName());
-			
+
 			items.add(item);
 		}
-		
+
 		return items;
 	}
-	
+
 	public Status getStatus(String orderStatus, String trxStatus, String pspTrxStatus) {
 		boolean warning = false;
 		boolean error = false;
 		boolean canceled = false;
-		
+
 		if(OrderStatus.CANCELED.name().equals(orderStatus)) {
 			canceled = true;
 		} else if(OrderStatus.ERROR.name().equals(orderStatus)) {
@@ -684,7 +699,7 @@ public class ACFrontendManager implements ACService {
 		} else if(OrderStatus.PREPAYMENT.name().equals(orderStatus)) {
 			warning = true;
 		}
-		
+
 		if(StringHelper.containsNonWhitespace(trxStatus)) {
 			if(trxStatus.contains(AccessTransactionStatus.CANCELED.name())) {
 				canceled = true;
@@ -692,7 +707,7 @@ public class ACFrontendManager implements ACService {
 				error = true;
 			}
 		}
-		
+
 		if(StringHelper.containsNonWhitespace(pspTrxStatus)) {
 			if(pspTrxStatus.contains(PSPTransactionStatus.ERROR.name())) {
 				error = true;
@@ -700,7 +715,7 @@ public class ACFrontendManager implements ACService {
 				warning = true;
 			}
 		}
-		
+
 		if(error) {
 			return Status.ERROR;
 		} else if (warning) {
@@ -709,14 +724,14 @@ public class ACFrontendManager implements ACService {
 			return Status.CANCELED;
 		} else {
 			return Status.OK;
-		}	
+		}
 	}
-	
+
 	/**
 	 * @return The current date without time
 	 */
 	private Date dateNow() {
 		return CalendarUtils.removeTime(new Date());
 	}
-	
+
 }
diff --git a/src/main/java/org/olat/resource/accesscontrol/manager/ACMethodDAO.java b/src/main/java/org/olat/resource/accesscontrol/manager/ACMethodDAO.java
index 7c7d74ef4ea347a6d6fbd8ed833da82ac01b4295..65e609bf5e9242da0a34d1584a2d3c631c531a05 100644
--- a/src/main/java/org/olat/resource/accesscontrol/manager/ACMethodDAO.java
+++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACMethodDAO.java
@@ -35,16 +35,11 @@ import javax.persistence.TypedQuery;
 
 import org.olat.core.commons.persistence.DB;
 import org.olat.core.commons.persistence.PersistenceHelper;
-import org.olat.core.gui.control.Event;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Roles;
 import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
-import org.olat.core.util.coordinate.CoordinatorManager;
-import org.olat.core.util.event.FrameworkStartedEvent;
-import org.olat.core.util.event.FrameworkStartupEventChannel;
-import org.olat.core.util.event.GenericEventListener;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceImpl;
 import org.olat.resource.accesscontrol.AccessControlModule;
@@ -60,49 +55,50 @@ import org.olat.resource.accesscontrol.model.OLATResourceAccess;
 import org.olat.resource.accesscontrol.model.OfferAccessImpl;
 import org.olat.resource.accesscontrol.model.TokenAccessMethod;
 import org.olat.resource.accesscontrol.provider.paypal.model.PaypalAccessMethod;
+import org.olat.shibboleth.manager.ShibbolethAutoAccessMethod;
+import org.springframework.beans.factory.InitializingBean;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 /**
- * 
+ *
  * Description:<br>
  * This class manages the methods available to access the resource.
  * As standard "static" (static as singleton), there is Token  and Free
  * based access.
- * 
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Service
-public class ACMethodDAO implements GenericEventListener {
-	
+public class ACMethodDAO implements InitializingBean {
+
 	private static final OLog log = Tracing.createLoggerFor(ACMethodDAO.class);
 
 	@Autowired
 	private DB dbInstance;
 	@Autowired
 	private AccessControlModule acModule;
-	
-	@Autowired
-	public ACMethodDAO(CoordinatorManager coordinatorManager) {
-		coordinatorManager.getCoordinator().getEventBus().registerFor(this, null, FrameworkStartupEventChannel.getStartupEventChannel());
-	}
 
 	@Override
-	public void event(Event event) {
-		if (event instanceof FrameworkStartedEvent && ((FrameworkStartedEvent) event).isEventOnThisNode()) {
-			enableMethod(TokenAccessMethod.class, acModule.isTokenEnabled());
-			enableMethod(FreeAccessMethod.class, acModule.isFreeEnabled());
-			enableMethod(PaypalAccessMethod.class, acModule.isPaypalEnabled());
-			dbInstance.commitAndCloseSession();
-		}
+	public void afterPropertiesSet() throws Exception {
+		enableMethod(TokenAccessMethod.class, acModule.isTokenEnabled());
+		enableMethod(FreeAccessMethod.class, acModule.isFreeEnabled());
+		enableMethod(PaypalAccessMethod.class, acModule.isPaypalEnabled());
+		enableAutoMethods(acModule.isAutoEnabled());
+		dbInstance.commitAndCloseSession();
+	}
+
+
+	public void enableAutoMethods(boolean autoEnabled) {
+		enableMethod(ShibbolethAutoAccessMethod.class, autoEnabled);
 	}
 
 	public void enableMethod(Class<? extends AccessMethod> type, boolean enable) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select method from ").append(type.getName()).append(" method");
-		
+
 		List<AccessMethod> methods = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), AccessMethod.class)
 				.getResultList();
@@ -131,7 +127,7 @@ public class ACMethodDAO implements GenericEventListener {
 
 	public boolean isValidMethodAvailable(OLATResource resource, Date atDate) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select count(access.method) from acofferaccess access ")
+		sb.append("select access.method from acofferaccess access ")
 			.append(" inner join access.offer offer")
 			.append(" inner join offer.resource oResource")
 			.append(" where access.valid=true")
@@ -142,16 +138,22 @@ public class ACMethodDAO implements GenericEventListener {
 			  .append(" and (offer.validTo is null or offer.validTo>=:atDate)");
 		}
 
-		TypedQuery<Number> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), Number.class);
+		TypedQuery<AccessMethod> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), AccessMethod.class);
 		query.setParameter("resourceKey", resource.getKey());
 		if(atDate != null) {
 			query.setParameter("atDate", atDate, TemporalType.TIMESTAMP);
 		}
 
-		Number methods = query.getSingleResult();
-		return methods.intValue() > 0;
+		List<AccessMethod> methods = query.getResultList();
+		List<AccessMethod> guiMethods = new ArrayList<>();
+		for (AccessMethod method: methods) {
+			if (method.isVisibleInGui()) {
+				guiMethods.add(method);
+			}
+		}
+		return !guiMethods.isEmpty();
 	}
-	
+
 	public List<AccessMethod> getAllMethods() {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select method from ").append(AbstractAccessMethod.class.getName()).append(" method");
@@ -164,12 +166,12 @@ public class ACMethodDAO implements GenericEventListener {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select method from ").append(AbstractAccessMethod.class.getName()).append(" method")
 			.append(" where method.valid=true and method.enabled=true");
-		
+
 		TypedQuery<AccessMethod> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), AccessMethod.class);
 		if(identity != null) {
 			//query.setLong("identityKey", identity.getKey());
 		}
-	
+
 		List<AccessMethod> methods = query.getResultList();
 		List<AccessMethod> allowedMethods = new ArrayList<AccessMethod>();
 		for(AccessMethod method:methods) {
@@ -187,7 +189,7 @@ public class ACMethodDAO implements GenericEventListener {
 		sb.append("select method from ").append(AbstractAccessMethod.class.getName()).append(" method")
 			.append(" where method.valid=true")
 			.append(" and method.class=").append(type.getName());
-		
+
 		TypedQuery<AccessMethod> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), AccessMethod.class);
 		List<AccessMethod> methods = query.getResultList();
 		return methods;
@@ -207,7 +209,28 @@ public class ACMethodDAO implements GenericEventListener {
 				.setParameter("offerKey", offer.getKey())
 				.getResultList();
 	}
-	
+
+	public List<OfferAccess> getValidOfferAccess(OLATResource resource, AccessMethod method) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select access from acofferaccess access")
+		  .append(" inner join fetch access.offer offer")
+		  .append(" inner join fetch access.method method")
+		  .append(" inner join fetch offer.resource resource")
+		  .append(" where resource.key=:resourceKey")
+		  .append(" and access.valid=true");
+		if(method != null) {
+			sb.append(" and access.method=:method");
+		}
+
+		TypedQuery<OfferAccess> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), OfferAccess.class);
+		query.setParameter("resourceKey", resource.getKey());
+		if(method != null) {
+			query.setParameter("method", method);
+		}
+		return query.getResultList();
+	}
+
 	public List<OfferAccess> getOfferAccess(Collection<Offer> offers, boolean valid) {
 		if(offers == null || offers.isEmpty()) return Collections.emptyList();
 
@@ -218,7 +241,7 @@ public class ACMethodDAO implements GenericEventListener {
 		  .append(" inner join fetch offer.resource resource")
 		  .append(" where offer.key in (:offersKey)")
 		  .append(" and access.valid=:valid");
-		
+
 		List<Long> offersKey = PersistenceHelper.toKeys(offers);
 		return dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), OfferAccess.class)
@@ -226,12 +249,12 @@ public class ACMethodDAO implements GenericEventListener {
 				.setParameter("valid", valid)
 				.getResultList();
 	}
-	
+
 	public List<OLATResourceAccess> getAccessMethodForResources(Collection<Long> resourceKeys,
 			String resourceType, String excludedResourceType, boolean valid, Date atDate) {
 
 		final int maxResourcesEntries = 250;//quicker to filter in java, numerous keys in "in" are slow
-		
+
 		StringBuilder sb = new StringBuilder();
 		sb.append("select access.method, resource, offer.price from acofferaccess access, ")
 			.append(OLATResourceImpl.class.getName()).append(" resource")
@@ -251,7 +274,7 @@ public class ACMethodDAO implements GenericEventListener {
 		if(StringHelper.containsNonWhitespace(excludedResourceType)) {
 			sb.append(" and not(oResource.resName=:excludedResourceType)");
 		}
-			
+
 		if(atDate != null) {
 			sb.append(" and (offer.validFrom is null or offer.validFrom<=:atDate)")
 				.append(" and (offer.validTo is null or offer.validTo>=:atDate)");
@@ -276,7 +299,7 @@ public class ACMethodDAO implements GenericEventListener {
 		if(StringHelper.containsNonWhitespace(excludedResourceType)) {
 			query.setParameter("excludedResourceType", excludedResourceType);
 		}
-		
+
 		List<Object[]> rawResults = query.getResultList();
 		Map<Long,OLATResourceAccess> rawResultsMap = new HashMap<Long,OLATResourceAccess>();
 		for(Object[] rawResult:rawResults) {
@@ -285,7 +308,10 @@ public class ACMethodDAO implements GenericEventListener {
 			if(resourceKeysSet != null && !resourceKeysSet.contains(resource.getKey())) {
 				continue;
 			}
-			
+			if(!method.isVisibleInGui()) {
+				continue;
+			}
+
 			Price price = (Price)rawResult[2];
 			if(rawResultsMap.containsKey(resource.getKey())) {
 				rawResultsMap.get(resource.getKey()).addBundle(price, method);
@@ -293,7 +319,7 @@ public class ACMethodDAO implements GenericEventListener {
 				rawResultsMap.put(resource.getKey(), new OLATResourceAccess(resource, price, method));
 			}
 		}
-		
+
 		return new ArrayList<OLATResourceAccess>(rawResultsMap.values());
 	}
 
@@ -305,7 +331,7 @@ public class ACMethodDAO implements GenericEventListener {
 		access.setValid(true);
 		return access;
 	}
-	
+
 	public OfferAccess save(OfferAccess link) {
 		if(link.getKey() == null) {
 			dbInstance.getCurrentEntityManager().persist(link);
@@ -314,22 +340,22 @@ public class ACMethodDAO implements GenericEventListener {
 		}
 		return link;
 	}
-	
+
 	public void delete(OfferAccess link) {
 		OfferAccessImpl access = (OfferAccessImpl)link;
 		access.setValid(false);
-	
+
 		if(link.getKey() == null) return;
 		dbInstance.updateObject(access);
 	}
-	
+
 	/**
 	 * Activate the token method if not already configured.
 	 */
 	protected void activateTokenMethod(boolean enable) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select method from actokenmethod method");
-		
+
 		List<AccessMethod> methods = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), AccessMethod.class)
 				.getResultList();
@@ -348,12 +374,12 @@ public class ACMethodDAO implements GenericEventListener {
 			}
 		}
 	}
-	
+
 	protected void activateFreeMethod(boolean enable) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select method from ").append(AbstractAccessMethod.class.getName())
 			.append(" method where method.class=").append(FreeAccessMethod.class.getName());
-		
+
 		TypedQuery<AccessMethod> query = dbInstance.getCurrentEntityManager().createQuery(sb.toString(), AccessMethod.class);
 		List<AccessMethod> methods = query.getResultList();
 		if(methods.isEmpty() && enable) {
@@ -371,4 +397,6 @@ public class ACMethodDAO implements GenericEventListener {
 			}
 		}
 	}
+
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/resource/accesscontrol/manager/ACOfferDAO.java b/src/main/java/org/olat/resource/accesscontrol/manager/ACOfferDAO.java
index 76dbcce208c2319949d840bd869ce3d2653b6073..398013d122564e92ec00f6a1b2f11fe7ac74c60e 100644
--- a/src/main/java/org/olat/resource/accesscontrol/manager/ACOfferDAO.java
+++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACOfferDAO.java
@@ -34,43 +34,55 @@ import javax.persistence.TypedQuery;
 import org.olat.core.commons.persistence.DB;
 import org.olat.resource.OLATResource;
 import org.olat.resource.accesscontrol.Offer;
+import org.olat.resource.accesscontrol.model.AccessMethod;
 import org.olat.resource.accesscontrol.model.OfferImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 /**
- * 
+ *
  * Description:<br>
- * 
+ *
  * <P>
  * Initial Date:  14 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Service
 public class ACOfferDAO {
-	
+
 	@Autowired
 	private DB dbInstance;
-	
+
 	public List<Offer> findOfferByResource(OLATResource resource, boolean valid, Date atDate) {
 		StringBuilder sb = new StringBuilder();
-		sb.append("select offer from acoffer offer")
-			.append(" left join fetch offer.resource resource")
-			.append(" where resource.key=:resourceKey")
-			.append(" and offer.valid=").append(valid);
+		sb.append("select offer, access.method from acofferaccess access ")
+				.append(" inner join access.offer offer")
+				.append(" left join offer.resource resource")
+				.append(" where resource.key=:resourceKey")
+				.append(" and offer.valid=").append(valid);
+
 		if(atDate != null) {
 			sb.append(" and (offer.validFrom is null or offer.validFrom<=:atDate)")
 			  .append(" and (offer.validTo is null or offer.validTo>=:atDate)");
 		}
 
-		TypedQuery<Offer> query = dbInstance.getCurrentEntityManager()
-				.createQuery(sb.toString(), Offer.class)
+		TypedQuery<Object[]> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Object[].class)
 				.setParameter("resourceKey", resource.getKey());
 		if(atDate != null) {
 			query.setParameter("atDate", atDate, TemporalType.TIMESTAMP);
 		}
-	
-		List<Offer> offers = query.getResultList();
+
+		List<Object[]> loadedObjects = query.getResultList();
+		List<Offer> offers = new ArrayList<>();
+		for(Object[] objects:loadedObjects) {
+			Offer offer = (Offer)objects[0];
+			AccessMethod method = (AccessMethod)objects[1];
+			if(method.isVisibleInGui()) {
+				offers.add(offer);
+			}
+		}
+
 		return offers;
 	}
 
@@ -90,7 +102,7 @@ public class ACOfferDAO {
 
 	public Set<Long> filterResourceWithOffer(Collection<Long> resourceKeys) {
 		if(resourceKeys == null || resourceKeys.isEmpty()) return Collections.emptySet();
-		
+
 		StringBuilder sb = new StringBuilder();
 		sb.append("select offer.resource.key from acoffer offer")
 		  .append(" inner join offer.resource resource")
@@ -99,7 +111,7 @@ public class ACOfferDAO {
 
 		Set<Long> resourceWithOffers = new HashSet<Long>();
 		List<Long> keys = new ArrayList<Long>(resourceKeys);
-		
+
 		//too much in with hibernate can generate a stack overflow
 		int hibernateInBatch = 500;
 		int firstResult = 0;
@@ -108,14 +120,14 @@ public class ACOfferDAO {
 			List<Long> inParameter = keys.subList(firstResult, toIndex);
 			query.setParameter("resourceKeys", inParameter);
 			firstResult += inParameter.size();
-			
+
 			List<Long> offerKeys = query.getResultList();
 			resourceWithOffers.addAll(offerKeys);
 		} while(firstResult < keys.size());
 
 		return resourceWithOffers;
 	}
-	
+
 	public Offer createOffer(OLATResource resource, String resourceName) {
 		OfferImpl offer = new OfferImpl();
 		Date now = new Date();
@@ -135,7 +147,7 @@ public class ACOfferDAO {
 		offer.setResourceTypeName(resourceTypeName);
 		return offer;
 	}
-	
+
 	public void deleteOffer(Offer offer) {
 		if(offer instanceof OfferImpl) {
 			((OfferImpl)offer).setValid(false);
diff --git a/src/main/java/org/olat/resource/accesscontrol/manager/ACOrderDAO.java b/src/main/java/org/olat/resource/accesscontrol/manager/ACOrderDAO.java
index 318e6b310fcae843dfd308723a954155edd5d86c..fd674822e7c61a5f1b1c721857403587ccb2741c 100644
--- a/src/main/java/org/olat/resource/accesscontrol/manager/ACOrderDAO.java
+++ b/src/main/java/org/olat/resource/accesscontrol/manager/ACOrderDAO.java
@@ -43,6 +43,7 @@ import org.olat.resource.accesscontrol.Order;
 import org.olat.resource.accesscontrol.OrderLine;
 import org.olat.resource.accesscontrol.OrderPart;
 import org.olat.resource.accesscontrol.OrderStatus;
+import org.olat.resource.accesscontrol.model.AccessMethod;
 import org.olat.resource.accesscontrol.model.OrderImpl;
 import org.olat.resource.accesscontrol.model.OrderLineImpl;
 import org.olat.resource.accesscontrol.model.OrderPartImpl;
@@ -52,21 +53,21 @@ import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 /**
- * 
+ *
  * Description:<br>
  * manager for the order. Orders are a part of the confirmation of an access
  * to a resource. The second part is the transaction.
- * 
+ *
  * <P>
  * Initial Date:  19 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Service
 public class ACOrderDAO {
-	
+
 	@Autowired
 	private DB dbInstance;
-	
+
 	public OrderImpl createOrder(Identity delivery) {
 		OrderImpl order = new OrderImpl();
 		Date now = new Date();
@@ -92,7 +93,7 @@ public class ACOrderDAO {
 		part.getOrderLines().add(line);
 		return line;
 	}
-	
+
 	private OrderLineImpl createOrderLine(Offer offer) {
 		OrderLineImpl line = new OrderLineImpl();
 		line.setCreationDate(new Date());
@@ -101,7 +102,7 @@ public class ACOrderDAO {
 		line.setTotal(line.getUnitPrice().clone());
 		return line;
 	}
-	
+
 	public Order save(Order order) {
 		if(order.getKey() == null) {
 			dbInstance.saveObject(order);
@@ -110,7 +111,7 @@ public class ACOrderDAO {
 		}
 		return order;
 	}
-	
+
 	public Order save(Order order, OrderStatus status) {
 		((OrderImpl)order).setOrderStatus(status);
 		if(order.getKey() == null) {
@@ -120,11 +121,11 @@ public class ACOrderDAO {
 		}
 		return order;
 	}
-	
+
 	public Order saveOneClick(Identity delivery, OfferAccess link) {
 		return saveOneClick(delivery, link, OrderStatus.PAYED);
 	}
-	
+
 	public Order saveOneClick(Identity delivery, OfferAccess link, OrderStatus status) {
 		OrderImpl order = createOrder(delivery);
 		order.setOrderStatus(status);
@@ -143,9 +144,10 @@ public class ACOrderDAO {
 		dbInstance.getCurrentEntityManager().persist(order);
 		dbInstance.getCurrentEntityManager().persist(part);
 		dbInstance.getCurrentEntityManager().persist(line);
+
 		return order;
 	}
-	
+
 	public List<Order> findOrdersByDelivery(Identity delivery, OrderStatus... status) {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select order from ").append(OrderImpl.class.getName()).append(" order")
@@ -153,7 +155,7 @@ public class ACOrderDAO {
 		if(status != null && status.length > 0) {
 			sb.append(" and order.orderStatusStr in (:status)");
 		}
-		
+
 		TypedQuery<Order> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Order.class)
 				.setParameter("deliveryKey", delivery.getKey());
@@ -164,22 +166,22 @@ public class ACOrderDAO {
 			}
 			query.setParameter("status", statusStr);
 		}
-	
+
 		List<Order> orders = query.getResultList();
 		return orders;
 	}
-	
+
 
 	/**
 	 * The method is optimized for our settings: 1 order -> 1 order part -> 1 order line
-	 * 
+	 *
 	 * @param resource
 	 * @param delivery
 	 * @return
 	 */
 	public int countNativeOrderItems(OLATResource resource, IdentityRef delivery, Long orderNr,
 			Date from, Date to, OrderStatus... status) {
-		
+
 		NativeQueryBuilder sb = new NativeQueryBuilder(1024, dbInstance);
 		sb.append("select count(o.order_id)")
 		  .append(" from o_ac_order o")
@@ -248,14 +250,14 @@ public class ACOrderDAO {
 			cal.set(Calendar.MILLISECOND, 0);
 			query.setParameter("to", cal.getTime(), TemporalType.TIMESTAMP);
 		}
-		
+
 		Object rawOrders = query.getSingleResult();
 		return rawOrders instanceof Number ? ((Number)rawOrders).intValue() : 0;
 	}
-	
+
 	/**
 	 * The method is optimized for our settings: 1 order -> 1 order part -> 1 order line
-	 * 
+	 *
 	 * @param resource
 	 * @param delivery
 	 * @return
@@ -263,7 +265,7 @@ public class ACOrderDAO {
 	public List<RawOrderItem> findNativeOrderItems(OLATResource resource, IdentityRef delivery, Long orderNr,
 			Date from, Date to, OrderStatus[] status, int firstResult, int maxResults,
 			List<UserPropertyHandler> userPropertyHandlers,  SortKey... orderBy) {
-		
+
 		NativeQueryBuilder sb = new NativeQueryBuilder(1024, dbInstance);
 		sb.append("select")
 		  .append("  o.order_id as order_id,")
@@ -293,11 +295,11 @@ public class ACOrderDAO {
 		  .append(" inner join o_ac_offer offer on (order_line.fk_offer_id=offer.offer_id)");
 		if(delivery == null) {
 			sb.append(" inner join o_bs_identity delivery on (delivery.id=o.fk_delivery_id)")
-			  .append(" inner join o_user delivery_user on (delivery_user.fk_identity=delivery.id)");	  
+			  .append(" inner join o_user delivery_user on (delivery_user.fk_identity=delivery.id)");
 		}
 		sb.append(" left join o_ac_paypal_transaction pspTrx on (o.order_id = pspTrx.order_id)")
 		  .append(" left join o_ac_transaction trx on (o.order_id = trx.fk_order_id)");
-		
+
 		boolean where = false;
 		if(resource != null) {
 			where = PersistenceHelper.appendAnd(sb, where);
@@ -323,7 +325,7 @@ public class ACOrderDAO {
 			where = PersistenceHelper.appendAnd(sb, where);
 			sb.append("o.order_id=:orderNr");
 		}
-		
+
 		sb.append(" group by o.order_id");
 		if(dbInstance.isOracle()) {
 			sb.append(", o.total_currency_code, o.total_amount, o.creationdate, o.order_status, o.fk_delivery_id");
@@ -380,7 +382,7 @@ public class ACOrderDAO {
 			cal.set(Calendar.MILLISECOND, 0);
 			query.setParameter("to", cal.getTime(), TemporalType.TIMESTAMP);
 		}
-		
+
 		if(maxResults > 0) {
 			query.setFirstResult(firstResult).setMaxResults(maxResults);
 		}
@@ -391,7 +393,7 @@ public class ACOrderDAO {
 		for(Object rawOrder:rawOrders) {
 			Object[] order = (Object[])rawOrder;
 			int pos = 0;
-			
+
 			Long orderKey = ((Number)order[pos++]).longValue();
 			String totalCurrencyCode = (String)order[pos++];
 			BigDecimal totalAmount = (BigDecimal)order[pos++];
@@ -402,7 +404,7 @@ public class ACOrderDAO {
 			String trxStatus = (String)order[pos++];
 			String trxMethodIds = (String)order[pos++];
 			String pspTrxStatus = (String)order[pos++];
-			
+
 			String username = null;
 			String[] userProperties = null;
 			if(numOfProperties > 0) {
@@ -413,8 +415,8 @@ public class ACOrderDAO {
 				for(int i=0; i<numOfProperties; i++) {
 					userProperties[i] = (String)order[pos++];
 				}
-			}	
-			
+			}
+
 			RawOrderItem item = new RawOrderItem(orderKey, orderKey.toString(), totalCurrencyCode, totalAmount,
 					creationDate, orderStatus, deliveryKey, resourceName,
 					trxStatus, trxMethodIds, pspTrxStatus, username, userProperties);
@@ -433,7 +435,7 @@ public class ACOrderDAO {
 		if(status != null && status.length > 0) {
 			sb.append(" and o.orderStatusStr in (:status)");
 		}
-		
+
 		TypedQuery<Order> query = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Order.class)
 				.setParameter("resourceKey", resource.getKey());
@@ -444,7 +446,36 @@ public class ACOrderDAO {
 			}
 			query.setParameter("status", statusStr);
 		}
-	
+
+		return query.getResultList();
+	}
+
+	public List<Order> findOrdersByResource(OLATResource resource, Identity identity, AccessMethod method) {
+		StringBuilder sb = new StringBuilder();
+		sb.append("select distinct(o) from ").append(OrderImpl.class.getName()).append(" o")
+			.append(" inner join o.parts orderPart ")
+			.append(" inner join orderPart.lines orderLine ")
+			.append(" inner join orderLine.offer offer ")
+			.append(" left join offer.offerAccess offeraccess ")
+			.append(" left join offeraccess.method method ")
+			.append(" where offer.resource.key=:resourceKey");
+		if(identity != null) {
+			sb.append(" and o.delivery.key=:deliveryKey");
+		}
+		if(method != null) {
+			sb.append(" and method.type=:methodKey");
+		}
+
+		TypedQuery<Order> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), Order.class)
+				.setParameter("resourceKey", resource.getKey());
+		if(identity != null) {
+			query.setParameter("deliveryKey", identity.getKey());
+		}
+		if (method != null) {
+			query.setParameter("methodKey", method.getKey());
+		}
+
 		return query.getResultList();
 	}
 
@@ -452,7 +483,7 @@ public class ACOrderDAO {
 		StringBuilder sb = new StringBuilder();
 		sb.append("select o from ").append(OrderImpl.class.getName()).append(" o")
 		  .append(" left join fetch o.parts parts where o.key=:orderKey");
-		
+
 		List<Order> orders = dbInstance.getCurrentEntityManager()
 				.createQuery(sb.toString(), Order.class)
 				.setParameter("orderKey", orderKey)
@@ -460,7 +491,7 @@ public class ACOrderDAO {
 		if(orders.isEmpty()) return null;
 		return orders.get(0);
 	}
-	
+
 	public Order loadOrderByNr(String orderNr) {
 		Long orderKey = new Long(orderNr);
 		return loadOrderByKey(orderKey);
diff --git a/src/main/java/org/olat/resource/accesscontrol/model/AbstractAccessMethod.java b/src/main/java/org/olat/resource/accesscontrol/model/AbstractAccessMethod.java
index 038e93f52ececb0453dece9f1b604325eb470ba1..ec1d9fd909d2063f5a984126319238cf0b859e72 100644
--- a/src/main/java/org/olat/resource/accesscontrol/model/AbstractAccessMethod.java
+++ b/src/main/java/org/olat/resource/accesscontrol/model/AbstractAccessMethod.java
@@ -42,25 +42,25 @@ import org.olat.core.id.ModifiedInfo;
 import org.olat.core.id.Persistable;
 
 /**
- * 
+ *
  * Description:<br>
  * The root object for access method. The concret implementation must inherent for this
- * and use the subclass of hibernate to map the object on the DB. 
- * 
+ * and use the subclass of hibernate to map the object on the DB.
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 
-@Entity  
-@Table(name="o_ac_method")  
-@Inheritance(strategy=InheritanceType.SINGLE_TABLE)  
-@DiscriminatorColumn(name="access_method", discriminatorType=DiscriminatorType.STRING)  
-@DiscriminatorValue(value="abstract")  
+@Entity
+@Table(name="o_ac_method")
+@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
+@DiscriminatorColumn(name="access_method", discriminatorType=DiscriminatorType.STRING)
+@DiscriminatorValue(value="abstract")
 public abstract class AbstractAccessMethod implements Persistable, AccessMethod, ModifiedInfo  {
 
 	private static final long serialVersionUID = 5656490927761461774L;
-	
+
 	@Id
 	@GeneratedValue(generator = "system-uuid")
 	@GenericGenerator(name = "system-uuid", strategy = "enhanced-sequence", parameters={
@@ -75,7 +75,7 @@ public abstract class AbstractAccessMethod implements Persistable, AccessMethod,
 	private Long key;
 	@Version
 	private int version = 0;
-	
+
 	@Temporal(TemporalType.TIMESTAMP)
 	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
 	private Date creationDate;
@@ -94,11 +94,11 @@ public abstract class AbstractAccessMethod implements Persistable, AccessMethod,
 	@Temporal(TemporalType.TIMESTAMP)
 	@Column(name="validto", nullable=true, insertable=true, updatable=true)
 	private Date validTo;
-	
+
 	public AbstractAccessMethod() {
 		//
 	}
-	
+
 	@Override
 	public Long getKey() {
 		return key;
@@ -108,6 +108,7 @@ public abstract class AbstractAccessMethod implements Persistable, AccessMethod,
 		this.key = key;
 	}
 
+	@Override
 	public Date getCreationDate() {
 		return creationDate;
 	}
@@ -124,7 +125,7 @@ public abstract class AbstractAccessMethod implements Persistable, AccessMethod,
 	public void setValid(boolean valid) {
 		this.valid = valid;
 	}
-	
+
 	@Override
 	public boolean isEnabled() {
 		return enabled;
@@ -134,9 +135,6 @@ public abstract class AbstractAccessMethod implements Persistable, AccessMethod,
 		this.enabled = enabled;
 	}
 
-	@Override
-	public abstract String getType();
-
 	@Override
 	public Date getLastModified() {
 		return lastModified;
@@ -162,12 +160,12 @@ public abstract class AbstractAccessMethod implements Persistable, AccessMethod,
 	public void setValidTo(Date validTo) {
 		this.validTo = validTo;
 	}
-	
+
 	@Override
 	public int hashCode() {
 		return getKey() == null ? 34688 : getKey().hashCode();
 	}
-	
+
 	@Override
 	public boolean equals(Object obj) {
 		if(this == obj) {
diff --git a/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.hbm.xml b/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.hbm.xml
index d825b051bd122de22c78fd8b52303aa05ac2fc0b..c53976e1df9414ee54099fe7a6977265ce6f3d1b 100644
--- a/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.hbm.xml
+++ b/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.hbm.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
 <hibernate-mapping default-lazy="false">
-	
+
 	<class name="org.olat.resource.accesscontrol.model.AbstractAccessMethod" table="o_ac_method">
 		<id name="key" column="method_id" type="long" unsaved-value="null">
 		<generator class="enhanced-sequence">
@@ -14,7 +14,7 @@
 		</generator>
 		</id>
     <discriminator column="access_method" type="string"/>
-		
+
 		<version name="version" access="field" column="version" type="int"/>
 		<property name="lastModified" column="lastmodified" type="timestamp" />
 	  <property name="creationDate" column="creationdate" type="timestamp" />
@@ -23,14 +23,18 @@
 
 	  <property name="validFrom" column="validfrom" type="timestamp" />
 	  <property name="validTo" column="validto" type="timestamp" />
-	  
+
 	  <subclass name="org.olat.resource.accesscontrol.model.TokenAccessMethod" discriminator-value="token.method">
-		
+
+		</subclass>
+
+	 	<subclass name="org.olat.shibboleth.manager.ShibbolethAutoAccessMethod" discriminator-value="auto.shib.method">
+
 		</subclass>
-		
+
 	  <subclass name="org.olat.resource.accesscontrol.model.FreeAccessMethod" discriminator-value="free.method">
-		
+
 		</subclass>
 	</class>
-	
+
 </hibernate-mapping>
diff --git a/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.java b/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.java
index 18c2cd8b750b48014d13db9e04ceec04faf880a9..ad18d440161db6e6fc1f69e3dd74304d8a4f5209 100644
--- a/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.java
+++ b/src/main/java/org/olat/resource/accesscontrol/model/AccessMethod.java
@@ -24,29 +24,31 @@ import java.util.Date;
 
 
 /**
- * 
+ *
  * Description:<br>
  * Interface for payment method
- * 
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 public interface AccessMethod {
-	
+
 	public Long getKey();
-	
+
 	public Date getCreationDate();
-	
+
 	public boolean isValid();
-	
+
 	public boolean isEnabled();
-	
+
 	public String getType();
-	
+
 	public boolean isNeedUserInteraction();
-	
+
 	public boolean isPaymentMethod();
 
 	public String getMethodCssClass();
+
+	public boolean isVisibleInGui();
 }
diff --git a/src/main/java/org/olat/resource/accesscontrol/model/FreeAccessMethod.java b/src/main/java/org/olat/resource/accesscontrol/model/FreeAccessMethod.java
index e26bf618927e5fb5eecd26f3d6fb1b7bffd75435..2823e12ea3c3547d5806e74567fc702603c594ba 100644
--- a/src/main/java/org/olat/resource/accesscontrol/model/FreeAccessMethod.java
+++ b/src/main/java/org/olat/resource/accesscontrol/model/FreeAccessMethod.java
@@ -27,16 +27,16 @@ import org.olat.resource.accesscontrol.provider.free.FreeAccessHandler;
 
 
 /**
- * 
+ *
  * Description:<br>
  * This a "static" payment method. There is only one instance.
- * 
+ *
  * <P>
  * Initial Date:  27 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Entity(name="acfreemethod")
-@DiscriminatorValue(value="free.method")  
+@DiscriminatorValue(value="free.method")
 public class FreeAccessMethod extends AbstractAccessMethod {
 
 	private static final long serialVersionUID = -6028245920419886453L;
@@ -55,9 +55,14 @@ public class FreeAccessMethod extends AbstractAccessMethod {
 	public boolean isNeedUserInteraction() {
 		return false;
 	}
-	
+
 	@Override
 	public boolean isPaymentMethod() {
 		return false;
 	}
+
+	@Override
+	public boolean isVisibleInGui() {
+		return true;
+	}
 }
diff --git a/src/main/java/org/olat/resource/accesscontrol/model/SystemACSecurityCallback.java b/src/main/java/org/olat/resource/accesscontrol/model/SystemACSecurityCallback.java
new file mode 100644
index 0000000000000000000000000000000000000000..743b9f590761671f13bd402b3d27cadcce884ff2
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/model/SystemACSecurityCallback.java
@@ -0,0 +1,36 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.model;
+
+/**
+ * Access only by the System. No user can use in the GUI.
+ *
+ * Initial date: 11.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class SystemACSecurityCallback implements AccessMethodSecurityCallback {
+
+	@Override
+	public boolean canUse() {
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/model/TokenAccessMethod.java b/src/main/java/org/olat/resource/accesscontrol/model/TokenAccessMethod.java
index af984686ec74eec157b5a219c4d525350c6a876f..74fcfcd99254de566a8f073e2017b7da2529c349 100644
--- a/src/main/java/org/olat/resource/accesscontrol/model/TokenAccessMethod.java
+++ b/src/main/java/org/olat/resource/accesscontrol/model/TokenAccessMethod.java
@@ -27,16 +27,16 @@ import org.olat.resource.accesscontrol.provider.token.TokenAccessHandler;
 
 
 /**
- * 
+ *
  * Description:<br>
  * This a "static" payment method. There is only one instance.
- * 
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Entity(name="actokenmethod")
-@DiscriminatorValue(value="token.method")  
+@DiscriminatorValue(value="token.method")
 public class TokenAccessMethod extends AbstractAccessMethod {
 
 	private static final long serialVersionUID = -8066110993424490600L;
@@ -44,20 +44,25 @@ public class TokenAccessMethod extends AbstractAccessMethod {
 	@Override
 	public String getType() {
 		return TokenAccessHandler.METHOD_TYPE;
-	}	
-	
+	}
+
 	@Override
 	public String getMethodCssClass() {
 		return TokenAccessHandler.METHOD_CSS_CLASS;
 	}
-	
+
 	@Override
 	public boolean isNeedUserInteraction() {
 		return true;
 	}
-	
+
 	@Override
 	public boolean isPaymentMethod() {
 		return false;
 	}
+
+	@Override
+	public boolean isVisibleInGui() {
+		return true;
+	}
 }
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/AdvanceOrder.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AdvanceOrder.java
new file mode 100644
index 0000000000000000000000000000000000000000..0756c1b6e18119b4aaf0fa9cb13de64531201fcb
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AdvanceOrder.java
@@ -0,0 +1,58 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto;
+
+import java.util.Date;
+
+import org.olat.core.id.CreateInfo;
+import org.olat.core.id.Identity;
+import org.olat.core.id.ModifiedInfo;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+
+/**
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public interface AdvanceOrder extends CreateInfo, ModifiedInfo {
+
+	public enum Status {
+		PENDING,
+		DONE}
+
+	public Long getKey();
+
+	public AccessMethod getMethod();
+
+	public IdentifierKey getIdentifierKey();
+
+	public String getIdentifierValue();
+
+	public Status getStatus();
+
+	public void setStatus(Status status);
+
+	public Date getStatusModified();
+
+	public void setStatusModified(Date modified);
+
+	public Identity getIdentity();
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/AdvanceOrderInput.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AdvanceOrderInput.java
new file mode 100644
index 0000000000000000000000000000000000000000..040eed76580bf41f38294c38e55ba536128936c0
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AdvanceOrderInput.java
@@ -0,0 +1,60 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto;
+
+import java.util.Set;
+
+import org.olat.core.id.Identity;
+import org.olat.resource.accesscontrol.provider.auto.model.AutoAccessMethod;
+
+/**
+ * Parameter Object to bundle the parameters to create the advance orders.
+ *
+ * Initial date: 17.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public interface AdvanceOrderInput {
+
+	/**
+	 * The class of the AccessMethod to choose.
+	 */
+	public Class<? extends AutoAccessMethod> getMethodClass();
+
+	/**
+	 * The identity for which the advance orders are created.
+	 */
+	public Identity getIdentity();
+
+	/**
+	 * The keys of the course attributes to search for.
+	 */
+	public Set<IdentifierKey> getKeys();
+
+	/**
+	 * The raw values to search.
+	 */
+	public String getRawValues();
+
+	/**
+	 * Type of the splitter to split the raw values in a set of single values.
+	 */
+	public String getSplitterType();
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..8b03cd1520475f6143aaa37d216eb1d939130642
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessHandler.java
@@ -0,0 +1,100 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.core.gui.UserRequest;
+import org.olat.core.gui.components.form.flexible.impl.Form;
+import org.olat.core.gui.control.WindowControl;
+import org.olat.core.id.Identity;
+import org.olat.core.id.Roles;
+import org.olat.resource.accesscontrol.OfferAccess;
+import org.olat.resource.accesscontrol.Order;
+import org.olat.resource.accesscontrol.OrderPart;
+import org.olat.resource.accesscontrol.method.AccessMethodHandler;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.model.AccessMethodSecurityCallback;
+import org.olat.resource.accesscontrol.model.PSPTransaction;
+import org.olat.resource.accesscontrol.model.SystemACSecurityCallback;
+import org.olat.resource.accesscontrol.ui.AbstractConfigurationMethodController;
+import org.olat.resource.accesscontrol.ui.FormController;
+
+/**
+ * AccessMethodHander for the automatic booking module. This module allows to
+ * save proposed assignments of users to a course by a third party service. The
+ * module effectively assigns the user to a course if the course for the saved
+ * key is available.
+ *
+ * Initial date: 11.08.2017<br>
+ *
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class AutoAccessHandler implements AccessMethodHandler {
+
+	private static final SystemACSecurityCallback SYSTEM_AC_SECURITY_CALLBACK = new SystemACSecurityCallback();
+
+	@Override
+	public boolean isPaymentMethod() {
+		return false;
+	}
+
+	@Override
+	public AccessMethodSecurityCallback getSecurityCallback(Identity identity, Roles roles) {
+		return SYSTEM_AC_SECURITY_CALLBACK;
+	}
+
+	@Override
+	public FormController createAccessController(UserRequest ureq, WindowControl wControl, OfferAccess link,
+			Form form) {
+		return null;
+	}
+
+	@Override
+	public AbstractConfigurationMethodController createConfigurationController(UserRequest ureq, WindowControl wControl,
+			OfferAccess link) {
+		return null;
+	}
+
+	@Override
+	public AbstractConfigurationMethodController editConfigurationController(UserRequest ureq, WindowControl wControl,
+			OfferAccess link) {
+		return null;
+	}
+
+	@Override
+	public FormController createTransactionDetailsController(UserRequest ureq, WindowControl wControl, Order order,
+			OrderPart part, AccessMethod method, Form form) {
+		return null;
+	}
+
+	@Override
+	public boolean checkArgument(OfferAccess link, Object argument) {
+		return true;
+	}
+
+	@Override
+	public List<PSPTransaction> getPSPTransactions(List<Order> orders) {
+		return Collections.emptyList();
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessManager.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessManager.java
new file mode 100644
index 0000000000000000000000000000000000000000..aa1890ad239aa3e4d57e60ffd99dffdedf638934
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/AutoAccessManager.java
@@ -0,0 +1,87 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto;
+
+import java.util.Collection;
+
+import org.olat.core.id.Identity;
+import org.olat.repository.RepositoryEntry;
+
+/**
+ * Manager for the automatic access control provider. This provider allows to
+ * save proposed assignments of users to a course by a third party service like
+ * Shibboleth, REST, LDAP etc. The module effectively assigns the user to a
+ * course if the course for the saved key is available.
+ *
+ * Initial date: 14.08.2017<br>
+ *
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public interface AutoAccessManager {
+
+	/**
+	 * Create a pending advance order for the combination of every identifier
+	 * key and every single identifier value in the input. You can specify an
+	 * parser to split the raw input value in singles values.
+	 *
+	 * @param input
+	 */
+	public void createAdvanceOrders(AdvanceOrderInput input);
+
+	/**
+	 * Load all pending advance orders of an identity.
+	 *
+	 * @param identity
+	 */
+	public Collection<AdvanceOrder> loadPendingAdvanceOrders(Identity identity);
+
+	/**
+	 * Load all pending advance orders for a ReposiotyEntry.
+	 *
+	 * @param resourse
+	 */
+	public Collection<AdvanceOrder> loadPendingAdvanceOrders(RepositoryEntry entry);
+
+	/**
+	 * Load all pending advance orders for the identity and try to grant access.
+	 *
+	 * @param identity
+	 */
+	public void grantAccessToCourse(Identity identity);
+
+	/**
+	 * Load all pending advance orders for the RepositoryEntry and try to grant access.
+	 *
+	 * @param repositoryEntry
+	 */
+	public void grantAccess(RepositoryEntry entry);
+
+	/**
+	 * Try to grant access to a resource for the identity in the pending advance
+	 * orders. If the resource is found and the access is granted, the status of the
+	 * advance order is set to done. If no resource for the identifier key and
+	 * value is found, the advance order remains pending.
+	 *
+	 * @param advanceOrders
+	 */
+	public void grantAccess(Collection<AdvanceOrder> advancedOrders);
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/IdentifierKey.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/IdentifierKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..30581a3b45178dce88ccad74e4519ba821fa4cbf
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/IdentifierKey.java
@@ -0,0 +1,34 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto;
+
+/**
+ * Options of course key which can be used to get the course by an advance order.
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public enum IdentifierKey {
+
+	internalId,
+	externalId,
+	externalRef
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/AdvanceOrderDAO.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/AdvanceOrderDAO.java
new file mode 100644
index 0000000000000000000000000000000000000000..23f70e0136aa1460a35ebe65655afffef30b7d05
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/AdvanceOrderDAO.java
@@ -0,0 +1,146 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.TypedQuery;
+
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.core.util.StringHelper;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder.Status;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.olat.resource.accesscontrol.provider.auto.model.AdvanceOrderImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+class AdvanceOrderDAO {
+
+	@Autowired
+	private DB dbInstance;
+
+	AdvanceOrder create(Identity identity, IdentifierKey key, String identifierValue, AccessMethod method) {
+		AdvanceOrderImpl advanceOrder = new AdvanceOrderImpl();
+		Date creationDate = new Date();
+		advanceOrder.setCreationDate(creationDate);
+		advanceOrder.setLastModified(creationDate);
+		advanceOrder.setIdentity(identity);
+		advanceOrder.setIdentifierKey(key);
+		advanceOrder.setIdentifierValue(identifierValue);
+		advanceOrder.setStatus(Status.PENDING);
+		advanceOrder.setStatusModified(creationDate);
+		advanceOrder.setMethod(method);
+		return advanceOrder;
+	}
+
+	boolean exists(Identity identity, IdentifierKey identifierKey, String identifierValue, AccessMethod method) {
+		Long numberOfAdvanceOrder = dbInstance.getCurrentEntityManager()
+				.createNamedQuery("exists", Long.class)
+				.setParameter("identityKey", identity.getKey())
+				.setParameter("identifierKey", identifierKey)
+				.setParameter("identifierValue", identifierValue)
+				.setParameter("methodKey", method.getKey())
+				.getSingleResult();
+		return numberOfAdvanceOrder != null && numberOfAdvanceOrder > 0;
+	}
+
+	Collection<AdvanceOrder> loadPendingAdvanceOrders(Identity identity) {
+		if (identity == null) return new ArrayList<>(0);
+
+		StringBuilder sb = new StringBuilder();
+		sb.append("select advanceOrder from advanceOrder advanceOrder")
+		  .append(" where advanceOrder.identity.key=:identityKey")
+		  .append("   and advanceOrder.status=:status");
+
+		List<AdvanceOrder> advanceOrder = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), AdvanceOrder.class)
+				.setParameter("identityKey", identity.getKey())
+				.setParameter("status", Status.PENDING)
+				.getResultList();
+
+		return advanceOrder;
+	}
+
+	Collection<AdvanceOrder> loadPendingAdvanceOrders(Map<IdentifierKey, String> identifiers) {
+		if (identifiers == null || identifiers.isEmpty()) return Collections.<AdvanceOrder>emptyList();
+
+		StringBuilder sb = new StringBuilder();
+		sb.append("select advanceOrder from advanceOrder advanceOrder")
+		  .append(" where advanceOrder.status=:status")
+		  .append("   and ((1 = 2)");
+
+		for (Map.Entry<IdentifierKey, String> entry : identifiers.entrySet()) {
+			if (entry.getKey() != null && StringHelper.containsNonWhitespace(entry.getValue())) {
+				sb.append(" or (advanceOrder.identifierKey=:").append(entry.getKey());
+				sb.append(" and advanceOrder.identifierValue=:").append(entry.getKey()).append(entry.getKey()).append(")");
+			}
+		}
+		sb.append(")");
+
+		TypedQuery<AdvanceOrder> query = dbInstance.getCurrentEntityManager()
+				.createQuery(sb.toString(), AdvanceOrder.class)
+				.setParameter("status", Status.PENDING);
+
+		for (Map.Entry<IdentifierKey, String> entry : identifiers.entrySet()) {
+			if (entry.getKey() != null && StringHelper.containsNonWhitespace(entry.getValue())) {
+				query.setParameter(entry.getKey().toString(), entry.getKey());
+				query.setParameter(entry.getKey().toString().concat(entry.getKey().toString()), entry.getValue());
+			}
+		}
+
+		return query.getResultList();
+	}
+
+	AdvanceOrder save(AdvanceOrder advanceOrder) {
+		if(advanceOrder.getKey() == null) {
+			dbInstance.getCurrentEntityManager().persist(advanceOrder);
+		} else {
+			advanceOrder.setLastModified(new Date());
+			advanceOrder = dbInstance.getCurrentEntityManager().merge(advanceOrder);
+		}
+		return advanceOrder;
+	}
+
+	AdvanceOrder accomplishAndSave(AdvanceOrder advanceOrder) {
+		if (advanceOrder == null) return advanceOrder;
+
+		advanceOrder.setStatus(Status.DONE);
+		advanceOrder.setStatusModified(new Date());
+		advanceOrder = save(advanceOrder);
+
+		return advanceOrder;
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/AutoAccessManagerImpl.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/AutoAccessManagerImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..541d928c85bfaaf62d3d0c6a6d45626281f6887f
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/AutoAccessManagerImpl.java
@@ -0,0 +1,206 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.List;
+import java.util.Map;
+
+import org.olat.basesecurity.GroupRoles;
+import org.olat.core.id.Identity;
+import org.olat.core.logging.OLog;
+import org.olat.core.logging.Tracing;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.manager.RepositoryEntryRelationDAO;
+import org.olat.resource.OLATResource;
+import org.olat.resource.accesscontrol.ACService;
+import org.olat.resource.accesscontrol.AccessControlModule;
+import org.olat.resource.accesscontrol.Offer;
+import org.olat.resource.accesscontrol.OfferAccess;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder.Status;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrderInput;
+import org.olat.resource.accesscontrol.provider.auto.AutoAccessManager;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.olat.resource.accesscontrol.provider.auto.model.AutoAccessMethod;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+/**
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Service
+public class AutoAccessManagerImpl implements AutoAccessManager {
+
+	private static final OLog log = Tracing.createLoggerFor(AutoAccessManagerImpl.class);
+
+	@Autowired
+	private IdentifierHandler identifierHandler;
+	@Autowired
+	private SplitterFactory splitterFactory;
+	@Autowired
+	private InputValidator inputValidator;
+	@Autowired
+	private AdvanceOrderDAO advanceOrderDAO;
+	@Autowired
+	private AccessControlModule acModule;
+	@Autowired
+	private ACService acService;
+	@Autowired
+	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
+
+	@Override
+	public void createAdvanceOrders(AdvanceOrderInput input) {
+		if (!inputValidator.isValid(input)) return;
+
+		IdentifierValueSplitter splitter = splitterFactory.getSplitter(input.getSplitterType());
+		Collection<String> values = splitter.split(input.getRawValues());
+		for (IdentifierKey key : input.getKeys()) {
+			for (String value : values) {
+				createAndPersistAdvanceOrderIfNotExist(input.getIdentity(), key, value, input.getMethodClass());
+			}
+		}
+	}
+
+	private void createAndPersistAdvanceOrderIfNotExist(Identity identity, IdentifierKey key, String value, Class<? extends AutoAccessMethod> type) {
+		List<AccessMethod> methods = acService.getAvailableMethodsByType(type);
+		AccessMethod method = methods.get(0);
+		if (doesNotExist(identity, key, value, method)) {
+			AdvanceOrder advanceOrder = advanceOrderDAO.create(identity, key, value, method);
+			advanceOrderDAO.save(advanceOrder);
+		}
+	}
+
+	private boolean doesNotExist(Identity identity, IdentifierKey key, String value, AccessMethod method) {
+		return !advanceOrderDAO.exists(identity, key, value, method);
+	}
+
+	@Override
+	public Collection<AdvanceOrder> loadPendingAdvanceOrders(Identity identity) {
+		return advanceOrderDAO.loadPendingAdvanceOrders(identity);
+	}
+
+	@Override
+	public Collection<AdvanceOrder> loadPendingAdvanceOrders(RepositoryEntry entry) {
+		if (entry == null) return Collections.<AdvanceOrder>emptyList();
+
+		Map<IdentifierKey, String> searchValues = new EnumMap<>(IdentifierKey.class);
+		for (IdentifierKey key: IdentifierKey.values()) {
+			String value = identifierHandler.getRepositoryEntryValue(key, entry);
+			searchValues.put(key, value);
+		}
+
+		return advanceOrderDAO.loadPendingAdvanceOrders(searchValues);
+	}
+
+	@Override
+	public void grantAccessToCourse(Identity identity) {
+		Collection<AdvanceOrder> pendingAdvanceOrders = loadPendingAdvanceOrders(identity);
+		grantAccess(pendingAdvanceOrders);
+	}
+
+	@Override
+	public void grantAccess(RepositoryEntry entry) {
+		Collection<AdvanceOrder> pendingAdvanceOrders = loadPendingAdvanceOrders(entry);
+		grantAccess(pendingAdvanceOrders);
+	}
+
+	@Override
+	public void grantAccess(Collection<AdvanceOrder> advanceOrders) {
+		if (!acModule.isAutoEnabled()) return;
+
+		for (AdvanceOrder advanceOrder : advanceOrders) {
+			try {
+				tryToGrantAccess(advanceOrder);
+			} catch (Exception e) {
+				log.error("Advance order can not be booked.", e);
+			}
+		}
+	}
+
+	private void tryToGrantAccess(AdvanceOrder advanceOrder) {
+		if (isAdvanceOrderAccomplished(advanceOrder))
+			return;
+
+		RepositoryEntry entry = findRepositoryEntry(advanceOrder);
+		if (entry != null) {
+			if (hasNoAccess(advanceOrder, entry)) {
+				OLATResource resource = entry.getOlatResource();
+				OfferAccess offerAccess = getOrCreateOfferAccess(resource, entry, advanceOrder.getMethod());
+				makeOrder(offerAccess, advanceOrder);
+			}
+			advanceOrderDAO.accomplishAndSave(advanceOrder);
+		}
+	}
+
+	private boolean isAdvanceOrderAccomplished(AdvanceOrder advanceOrder) {
+		return !Status.PENDING.equals(advanceOrder.getStatus());
+	}
+
+	private RepositoryEntry findRepositoryEntry(AdvanceOrder advanceOrder) {
+		IdentifierKey identifierKey = advanceOrder.getIdentifierKey();
+		String identifierValue = advanceOrder.getIdentifierValue();
+		return identifierHandler.findRepositoryEntry(identifierKey, identifierValue);
+	}
+
+	private boolean hasNoAccess(AdvanceOrder advanceOrder, RepositoryEntry entry) {
+		Identity identity = advanceOrder.getIdentity();
+		boolean hasNoAccess = true;
+		if (repositoryEntryRelationDao.hasRole(identity, entry, GroupRoles.participant.name())) {
+			hasNoAccess = false;
+		}
+		return hasNoAccess;
+	}
+
+	private OfferAccess getOrCreateOfferAccess(OLATResource resource, RepositoryEntry entry, AccessMethod method) {
+		OfferAccess offerAccess;
+		List<OfferAccess> offerAccesses = acService.getValidOfferAccess(resource, method);
+		if (offerAccesses.isEmpty()) {
+			offerAccess = createOfferAccess(resource, entry, method);
+		} else {
+			offerAccess = offerAccesses.get(0);
+		}
+		return offerAccess;
+	}
+
+	private OfferAccess createOfferAccess(OLATResource resource, RepositoryEntry entry, AccessMethod method) {
+		OfferAccess offerAccess;
+		String displayName = entry.getDisplayname();
+		Offer offer = acService.createOffer(resource, displayName);
+		offer.setAutoBooking(true);
+		offerAccess = acService.createOfferAccess(offer, method);
+		acService.save(offer);
+		acService.saveOfferAccess(offerAccess);
+		return offerAccess;
+	}
+
+	private void makeOrder(OfferAccess offerAccess, AdvanceOrder advanceOrder) {
+		Identity identity = advanceOrder.getIdentity();
+		acService.accessResource(identity, offerAccess, null);
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalIdHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalIdHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..9da61199d7e5bac2d82db9e6d2fee0a5b873383d
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalIdHandler.java
@@ -0,0 +1,66 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Initial date: 15.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+class ExternalIdHandler implements IdentifierKeyHandler {
+
+	@Autowired
+	private RepositoryService repositoryService;
+
+	@Override
+	public IdentifierKey getItentifierKey() {
+		return IdentifierKey.internalId;
+	}
+
+	@Override
+	public List<RepositoryEntry> find(String value) {
+		List<RepositoryEntry> entries = Collections.emptyList();;
+
+		try {
+			entries = repositoryService.loadRepositoryEntriesByExternalId(value);
+		} catch (Exception e) {
+			// nothing to add
+		}
+
+		return entries;
+	}
+
+	@Override
+	public String getRepositoryEntryValue(RepositoryEntry entry) {
+		return entry.getExternalId();
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalRefHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalRefHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..80362f2d0900e69889f8158669f08dcf82da5156
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalRefHandler.java
@@ -0,0 +1,66 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Initial date: 15.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+class ExternalRefHandler implements IdentifierKeyHandler {
+
+	@Autowired
+	private RepositoryService repositoryService;
+
+	@Override
+	public IdentifierKey getItentifierKey() {
+		return IdentifierKey.externalRef;
+	}
+
+	@Override
+	public List<RepositoryEntry> find(String value) {
+		List<RepositoryEntry> entries = Collections.<RepositoryEntry>emptyList();;
+
+		try {
+			entries = repositoryService.loadRepositoryEntriesByExternalRef(value);
+		} catch (Exception e) {
+			// nothing to add
+		}
+
+		return entries;
+	}
+
+	@Override
+	public String getRepositoryEntryValue(RepositoryEntry entry) {
+		return entry.getExternalRef();
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..4ff05a050c1894dd71205b5249c61b7fd5663227
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierHandler.java
@@ -0,0 +1,83 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+import org.olat.repository.RepositoryEntry;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Initial date: 15.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+class IdentifierHandler {
+
+	@Autowired
+    private Collection<IdentifierKeyHandler> loadedHandlers;
+
+    private Map<IdentifierKey, IdentifierKeyHandler> handlers = new HashMap<>();
+
+    @PostConstruct
+    void initHandlerCache() {
+        for(IdentifierKeyHandler handler : loadedHandlers) {
+            handlers.put(handler.getItentifierKey(), handler);
+        }
+    }
+
+    /**
+     * Finds the RepositoryEntry for a given identifier key and value.
+     *
+     * @param key
+     * @param value
+     * @returns the course or null if not found or too many found
+     */
+    RepositoryEntry findRepositoryEntry(IdentifierKey key, String value) {
+		List<RepositoryEntry> entries = handlers.get(key).find(value);
+
+		RepositoryEntry entry = null;
+		if (entries.size() == 1) {
+			entry = entries.get(0);
+		}
+		return entry;
+    }
+
+    /**
+     * Takes a RepostoryEntry and returns the value for a given IdenifierKey
+     *
+     * @param key
+     * @param entry
+     * @return
+     */
+	String getRepositoryEntryValue(IdentifierKey key, RepositoryEntry entry) {
+		return handlers.get(key).getRepositoryEntryValue(entry);
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierKeyHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierKeyHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..89521e6aa503dc5c4ffea6a049432dd011b12ca0
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierKeyHandler.java
@@ -0,0 +1,57 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.List;
+
+import org.olat.repository.RepositoryEntry;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+
+/**
+ * Strategy to handle the IdentifierKeys
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+interface IdentifierKeyHandler {
+
+	/**
+	 * Specifies for which identifier the handler can be used.
+	 */
+	public IdentifierKey getItentifierKey();
+
+	/**
+	 * Find the RepositoryEntries by a given value.
+	 *
+	 * @param value the value to search for
+	 * @return the RepositoryEntries found for the given value
+	 */
+	public List<RepositoryEntry> find(String value);
+
+	/**
+	 * Finds the appropriate Value in the RepositoryEntry.
+	 *
+	 * @param entry
+	 * @return
+	 */
+	public String getRepositoryEntryValue(RepositoryEntry entry);
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierValueSplitter.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierValueSplitter.java
new file mode 100644
index 0000000000000000000000000000000000000000..29009b3d4f1dd02a9fcc6cf4a5be276643b7a2de
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierValueSplitter.java
@@ -0,0 +1,36 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.Collection;
+
+/**
+ * Split the raw identifier value in single values.
+ *
+ * Initial date: 23.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+interface IdentifierValueSplitter {
+
+	public String getType();
+
+	public Collection<String> split(String rawValue);
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/InputValidator.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/InputValidator.java
new file mode 100644
index 0000000000000000000000000000000000000000..4e85e307fcd07374324e90daf2e7c28889a86f10
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/InputValidator.java
@@ -0,0 +1,58 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrderInput;
+import org.springframework.stereotype.Component;
+
+/**
+ * Helper to validate the values of the AdvanceOrderInput.
+ *
+ * Initial date: 17.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+class InputValidator {
+
+	boolean isValid(AdvanceOrderInput input) {
+		boolean isValid = true;
+
+		if (input == null) {
+			isValid &= false;
+		} else {
+			isValid &= notNull(input.getIdentity());
+			isValid &= notNull(input.getMethodClass());
+			isValid &= notNull(input.getRawValues());
+
+			if (input.getKeys() == null) {
+				isValid &= false;
+			} else {
+				isValid &= input.getKeys().isEmpty() ? false: true;
+			}
+		}
+
+		return isValid;
+	}
+
+	private boolean notNull(Object input) {
+		return input == null? false: true;
+	}
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/InternalIdHandler.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/InternalIdHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..5c437f3d197fde284e8c5c84c65f3657a8729b3b
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/InternalIdHandler.java
@@ -0,0 +1,69 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ * Initial date: 15.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+class InternalIdHandler implements IdentifierKeyHandler {
+
+	@Autowired
+	private RepositoryService repositoryService;
+
+	@Override
+	public IdentifierKey getItentifierKey() {
+		return IdentifierKey.externalId;
+	}
+
+	@Override
+	public List<RepositoryEntry> find(String value) {
+		List<RepositoryEntry> entries = Collections.<RepositoryEntry>emptyList();
+
+		try {
+			Long repositoryEntryKey = Long.parseLong(value);
+			RepositoryEntry entry = repositoryService.loadByKey(repositoryEntryKey);
+			if (entry != null) {
+				entries.add(entry);
+			}
+		} catch (Exception e) {
+			// nothing to add
+		}
+
+		return entries;
+	}
+
+	@Override
+	public String getRepositoryEntryValue(RepositoryEntry entry) {
+		return Long.toString(entry.getKey());
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/SemicolonSplitter.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/SemicolonSplitter.java
new file mode 100644
index 0000000000000000000000000000000000000000..15aa6fad3a9ff783899d94f39d3f1019f1b3b685
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/SemicolonSplitter.java
@@ -0,0 +1,52 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+import org.springframework.stereotype.Component;
+
+
+/**
+ *
+ * Initial date: 23.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+public class SemicolonSplitter implements IdentifierValueSplitter {
+
+	public static final String TYPE = "Semicolon";
+
+	@Override
+	public String getType() {
+		return TYPE;
+	}
+
+	@Override
+	public Collection<String> split(String rawValue) {
+		if (rawValue == null) return Collections.<String>emptyList();
+
+		return Arrays.asList(rawValue.split(";"));
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/SplitterFactory.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/SplitterFactory.java
new file mode 100644
index 0000000000000000000000000000000000000000..21127752c0623c4b4ecbce11137e1ce9851d32ea
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/manager/SplitterFactory.java
@@ -0,0 +1,59 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.PostConstruct;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Initial date: 23.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+public class SplitterFactory {
+
+	@Autowired
+    private List<IdentifierValueSplitter> services;
+
+    private static final Map<String, IdentifierValueSplitter> cache = new HashMap<>();
+
+    @PostConstruct
+    void initIdentifierValueSplitterCache() {
+        for(IdentifierValueSplitter service : services) {
+            cache.put(service.getType(), service);
+        }
+    }
+
+    public IdentifierValueSplitter getSplitter(String type) {
+        IdentifierValueSplitter splitter = cache.get(type);
+        if (splitter == null) {
+        	splitter = cache.get(SemicolonSplitter.TYPE);
+        }
+        return splitter;
+    }
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/model/AdvanceOrderImpl.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/model/AdvanceOrderImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..412ef8e246149acd93e0ff79bb26a6688456d4b4
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/model/AdvanceOrderImpl.java
@@ -0,0 +1,210 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.model;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.FetchType;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.olat.basesecurity.IdentityImpl;
+import org.olat.core.id.Identity;
+import org.olat.core.id.Persistable;
+import org.olat.resource.accesscontrol.model.AbstractAccessMethod;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+
+/**
+ *
+ * Initial date: 14.08.2017<br>
+ *
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Entity(name="advanceOrder")
+@Table(name="o_ac_auto_advance_order")
+@NamedQueries(
+	@NamedQuery(name="exists", query=
+			  "select count(*) "
+			+ "from advanceOrder ao "
+			+ "where ao.identity.key =:identityKey "
+			+ "and ao.identifierKey =:identifierKey "
+			+ "and ao.identifierValue =:identifierValue "
+			+ "and ao.method.key =:methodKey")
+)
+public class AdvanceOrderImpl implements Persistable, AdvanceOrder {
+
+	private static final long serialVersionUID = -536425559285612562L;
+
+	@Id
+	@GeneratedValue(strategy = GenerationType.IDENTITY)
+	@Column(name="id", nullable=false, unique=true, insertable=true, updatable=false)
+	private Long key;
+
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="creationdate", nullable=false, insertable=true, updatable=false)
+	private Date creationDate;
+	@Temporal(TemporalType.TIMESTAMP)
+	@Column(name="lastmodified", nullable=false, insertable=true, updatable=true)
+	private Date lastModified;
+
+	@Enumerated(EnumType.STRING)
+	@Column(name="a_identifier_key", nullable=false, insertable=true, updatable=false)
+	private IdentifierKey identifierKey;
+	@Column(name="a_identifier_value", nullable=false, insertable=true, updatable=false)
+	private String identifierValue;
+
+	@Enumerated(EnumType.STRING)
+	@Column(name="a_status", nullable=false, insertable=true, updatable=true)
+	private Status status;
+	@Column(name="a_status_modified", nullable=false, insertable=true, updatable=true)
+	private Date statusModified;
+
+	@ManyToOne(targetEntity=IdentityImpl.class, fetch=FetchType.LAZY, optional=false)
+	@JoinColumn(name="fk_identity", nullable=false, insertable=true, updatable=false)
+	private Identity identity;
+
+	@ManyToOne(targetEntity=AbstractAccessMethod.class,fetch=FetchType.LAZY,optional=false)
+	@JoinColumn(name="fk_method", nullable=false, insertable=true, updatable=false)
+	private AccessMethod method;
+
+	@Override
+	public Date getCreationDate() {
+		return creationDate;
+	}
+
+	public void setCreationDate(Date creationDate) {
+		this.creationDate = creationDate;
+	}
+
+	@Override
+	public Date getLastModified() {
+		return lastModified;
+	}
+
+	@Override
+	public void setLastModified(Date date) {
+		lastModified = date;
+	}
+
+	@Override
+	public Long getKey() {
+		return key;
+	}
+
+	public void setKey(Long key) {
+		this.key = key;
+	}
+
+	@Override
+	public AccessMethod getMethod() {
+		return method;
+	}
+
+	public void setMethod(AccessMethod method) {
+		this.method = method;
+	}
+
+	@Override
+	public IdentifierKey getIdentifierKey() {
+		return identifierKey;
+	}
+
+	public void setIdentifierKey(IdentifierKey key) {
+		this.identifierKey = key;
+	}
+
+	@Override
+	public String getIdentifierValue() {
+		return identifierValue;
+	}
+
+	public void setIdentifierValue(String identifierValue) {
+		this.identifierValue = identifierValue;
+	}
+
+	@Override
+	public Status getStatus() {
+		return status;
+	}
+
+	@Override
+	public void setStatus(Status status) {
+		this.status = status;
+	}
+
+	@Override
+	public Date getStatusModified() {
+		return statusModified;
+	}
+
+	@Override
+	public void setStatusModified(Date statusModified) {
+		this.statusModified = statusModified;
+	}
+
+	@Override
+	public Identity getIdentity() {
+		return identity;
+	}
+
+	public void setIdentity(Identity identity) {
+		this.identity = identity;
+	}
+
+	@Override
+	public boolean equalsByPersistableKey(Persistable persistable) {
+		return equals(persistable);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((identifierKey == null) ? 0 : identifierKey.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if(this == obj) {
+			return true;
+		} else if(obj instanceof AdvanceOrderImpl) {
+			AdvanceOrderImpl other = (AdvanceOrderImpl)obj;
+			return getKey() != null && getKey().equals(other.getKey());
+		}
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/auto/model/AutoAccessMethod.java b/src/main/java/org/olat/resource/accesscontrol/provider/auto/model/AutoAccessMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..252d4ce401324396bda4acdfc65c321d58689420
--- /dev/null
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/auto/model/AutoAccessMethod.java
@@ -0,0 +1,54 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.model;
+
+import org.olat.resource.accesscontrol.model.AbstractAccessMethod;
+
+/**
+ *
+ * Initial date: 11.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public abstract class AutoAccessMethod extends AbstractAccessMethod {
+
+	private static final long serialVersionUID = -3537267568105282400L;
+
+	@Override
+	public boolean isNeedUserInteraction() {
+		return false;
+	}
+
+	@Override
+	public boolean isPaymentMethod() {
+		return false;
+	}
+
+	@Override
+	public String getMethodCssClass() {
+		return null;
+	}
+
+	@Override
+	public boolean isVisibleInGui() {
+		return false;
+	}
+
+}
diff --git a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/model/PaypalAccessMethod.java b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/model/PaypalAccessMethod.java
index dfd12138117ae860f0bbcb11bbf9eefdf98c4732..4fc2d30954f8b2cc0a92cdfb2cf24e35c94d796a 100644
--- a/src/main/java/org/olat/resource/accesscontrol/provider/paypal/model/PaypalAccessMethod.java
+++ b/src/main/java/org/olat/resource/accesscontrol/provider/paypal/model/PaypalAccessMethod.java
@@ -28,37 +28,42 @@ import org.olat.resource.accesscontrol.provider.paypal.PaypalAccessHandler;
 
 
 /**
- * 
+ *
  * Description:<br>
  * This a paypal payment method.
- * 
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 @Entity(name="acpaypalmethod")
-@DiscriminatorValue(value="paypal.method") 
+@DiscriminatorValue(value="paypal.method")
 public class PaypalAccessMethod extends AbstractAccessMethod {
-	
+
 	private static final long serialVersionUID = 7682228653442368290L;
 
 	@Override
 	public String getType() {
 		return PaypalAccessHandler.METHOD_TYPE;
-	}	
-	
+	}
+
 	@Override
 	public String getMethodCssClass() {
 		return PaypalAccessHandler.METHOD_CSS_CLASS;
 	}
-	
+
 	@Override
 	public boolean isNeedUserInteraction() {
 		return true;
 	}
-	
+
 	@Override
 	public boolean isPaymentMethod() {
 		return true;
 	}
+
+	@Override
+	public boolean isVisibleInGui() {
+		return true;
+	}
 }
diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java b/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java
index 415479233476a4b7c64437eb7774016104d4d5ab..5f30e7fa5e75db21fa9bfb89c99f6bbee46b72dc 100644
--- a/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java
+++ b/src/main/java/org/olat/resource/accesscontrol/ui/AccessConfigurationController.java
@@ -55,10 +55,10 @@ import org.olat.resource.accesscontrol.model.OfferImpl;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
- * 
+ *
  * Description:<br>
- * 
- * 
+ *
+ *
  * <P>
  * Initial Date:  14 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
@@ -72,21 +72,21 @@ public class AccessConfigurationController extends FormBasicController {
 	private CloseableModalController cmc;
 	private FormLayoutContainer confControllerContainer;
 	private AbstractConfigurationMethodController newMethodCtrl, editMethodCtrl;
-	
+
 	private final List<AccessInfo> confControllers = new ArrayList<AccessInfo>();
-	
+
 	private final boolean embbed;
 	private final boolean emptyConfigGrantsFullAccess;
 	private boolean allowPaymentMethod;
 	private final boolean editable;
-	
+
 	private final Formatter formatter;
-	
+
 	@Autowired
 	private ACService acService;
 	@Autowired
 	private AccessControlModule acModule;
-	
+
 	public AccessConfigurationController(UserRequest ureq, WindowControl wControl, OLATResource resource,
 			String displayName, boolean allowPaymentMethod, boolean editable) {
 		super(ureq, wControl, "access_configuration");
@@ -96,16 +96,16 @@ public class AccessConfigurationController extends FormBasicController {
 		this.allowPaymentMethod = allowPaymentMethod;
 		embbed = false;
 		this.editable = editable;
-		emptyConfigGrantsFullAccess = true; 
+		emptyConfigGrantsFullAccess = true;
 		formatter = Formatter.getInstance(getLocale());
-		
+
 		initForm(ureq);
 	}
-		
+
 	public AccessConfigurationController(UserRequest ureq, WindowControl wControl, OLATResource resource,
 			String displayName, boolean allowPaymentMethod, boolean editable, Form form) {
 		super(ureq, wControl, FormBasicController.LAYOUT_CUSTOM, "access_configuration", form);
-		
+
 		this.editable = editable;
 		this.resource = resource;
 		this.displayName = displayName;
@@ -113,10 +113,10 @@ public class AccessConfigurationController extends FormBasicController {
 		embbed = true;
 		emptyConfigGrantsFullAccess = false;
 		formatter = Formatter.getInstance(getLocale());
-		
+
 		initForm(ureq);
 	}
-	
+
 	public int getNumOfBookingConfigurations() {
 		return confControllers.size();
 	}
@@ -134,7 +134,7 @@ public class AccessConfigurationController extends FormBasicController {
 				if(handler.isPaymentMethod() && !allowPaymentMethod) {
 					continue;
 				}
-				
+
 				String title = handler.getMethodName(getLocale());
 				FormLink add = uifactory.addFormLink("create." + handler.getType(), title, null, formLayout, Link.LINK | Link.NONTRANSLATED);
 				add.setUserObject(method);
@@ -144,16 +144,16 @@ public class AccessConfigurationController extends FormBasicController {
 			}
 			((FormLayoutContainer)formLayout).contextPut("methods", addMethods);
 		}
-		
+
 		String confPage = velocity_root + "/configuration_list.html";
 		confControllerContainer = FormLayoutContainer.createCustomFormLayout("conf-controllers", getTranslator(), confPage);
 		confControllerContainer.setRootForm(mainForm);
 		formLayout.add(confControllerContainer);
-		
+
 		loadConfigurations();
-		
+
 		confControllerContainer.contextPut("confControllers", confControllers);
-		
+
 		if(!embbed) {
 			setFormTitle("accesscontrol.title");
 
@@ -166,14 +166,14 @@ public class AccessConfigurationController extends FormBasicController {
 				uifactory.addFormSubmitButton("save", buttonGroupLayout);
 			}
 		}
-		
-		confControllerContainer.contextPut("emptyConfigGrantsFullAccess", Boolean.valueOf(emptyConfigGrantsFullAccess));		
+
+		confControllerContainer.contextPut("emptyConfigGrantsFullAccess", Boolean.valueOf(emptyConfigGrantsFullAccess));
 	}
-	
+
 	public void setAllowPaymentMethod(boolean allowPayment) {
 		this.allowPaymentMethod = allowPayment;
 	}
-	
+
 	public boolean isPaymentMethodInUse() {
 		boolean paymentMethodInUse = false;
 		for(AccessInfo info:confControllers) {
@@ -181,7 +181,7 @@ public class AccessConfigurationController extends FormBasicController {
 		}
 		return paymentMethodInUse;
 	}
-	
+
 	@Override
 	protected void doDispose() {
 		//
@@ -223,7 +223,7 @@ public class AccessConfigurationController extends FormBasicController {
 			super.event(ureq, source, event);
 		}
 	}
-	
+
 	private void cleanUp() {
 		removeAsListenerAndDispose(editMethodCtrl);
 		removeAsListenerAndDispose(newMethodCtrl);
@@ -257,7 +257,7 @@ public class AccessConfigurationController extends FormBasicController {
 	@Override
 	public void formOK(UserRequest ureq) {
 		Map<String,FormItem> formItemMap = confControllerContainer.getFormComponents();
-		
+
 		List<OfferAccess> links = new ArrayList<OfferAccess>();
 		for(AccessInfo info:confControllers) {
 			FormItem dateFrom = formItemMap.get("from_" + info.getLink().getKey());
@@ -266,18 +266,18 @@ public class AccessConfigurationController extends FormBasicController {
 				info.getLink().setValidFrom(from);
 				info.getLink().getOffer().setValidFrom(from);
 			}
-			
+
 			FormItem dateTo = formItemMap.get("to_" + info.getLink().getKey());
 			if(dateTo instanceof DateChooser) {
 				Date to = ((DateChooser)dateTo).getDate();
 				info.getLink().setValidTo(to);
 				info.getLink().getOffer().setValidTo(to);
 			}
-			
+
 			links.add(info.getLink());
 		}
 	}
-	
+
 	protected void loadConfigurations() {
 		List<Offer> offers = acService.findOfferByResource(resource, true, null);
 		for(Offer offer:offers) {
@@ -287,7 +287,7 @@ public class AccessConfigurationController extends FormBasicController {
 			}
 		}
 	}
-	
+
 	protected void replace(OfferAccess link) {
 		boolean updated = false;
 		for(AccessInfo confController:confControllers) {
@@ -296,41 +296,41 @@ public class AccessConfigurationController extends FormBasicController {
 				updated = true;
 			}
 		}
-		
+
 		if(!updated) {
 			addConfiguration(link);
 		} else {
 			confControllerContainer.setDirty(true);
 		}
 	}
-	
+
 	protected void addConfiguration(OfferAccess link) {
 		AccessMethodHandler handler = acModule.getAccessMethodHandler(link.getMethod().getType());
 		AccessInfo infos = new AccessInfo(handler.getMethodName(getLocale()), handler.isPaymentMethod(), null, link);
 		confControllers.add(infos);
-		
+
 		if(editable) {
 			FormLink editLink = uifactory.addFormLink("edit_" + link.getKey(), "edit", "edit", null, confControllerContainer, Link.BUTTON_SMALL);
 			editLink.setUserObject(infos);
 			editLink.setIconLeftCSS("o_icon o_icon-fw o_icon_edit");
 			confControllerContainer.add(editLink.getName(), editLink);
-			
+
 			FormLink delLink = uifactory.addFormLink("del_" + link.getKey(), "delete", "delete", null, confControllerContainer, Link.BUTTON_SMALL);
 			delLink.setUserObject(infos);
 			delLink.setIconLeftCSS("o_icon o_icon-fw o_icon_delete_item");
 			confControllerContainer.add(delLink.getName(), delLink);
 		}
 	}
-	
+
 	private void editMethod(UserRequest ureq, AccessInfo infos) {
 		OfferAccess link = infos.getLink();
-		
+
 		removeAsListenerAndDispose(editMethodCtrl);
 		AccessMethodHandler handler = acModule.getAccessMethodHandler(link.getMethod().getType());
 		if (handler != null) {
 			editMethodCtrl = handler.editConfigurationController(ureq, getWindowControl(), link);
 		}
-		
+
 		if(editMethodCtrl != null) {
 			listenTo(editMethodCtrl);
 
@@ -340,11 +340,11 @@ public class AccessConfigurationController extends FormBasicController {
 			listenTo(cmc);
 		}
 	}
-	
+
 	protected void addMethod(UserRequest ureq, AccessMethod method) {
 		Offer offer = acService.createOffer(resource, displayName);
 		OfferAccess link = acService.createOfferAccess(offer, method);
-		
+
 		removeAsListenerAndDispose(newMethodCtrl);
 		AccessMethodHandler handler = acModule.getAccessMethodHandler(link.getMethod().getType());
 		if (handler != null) {
@@ -362,29 +362,29 @@ public class AccessConfigurationController extends FormBasicController {
 			addConfiguration(newLink);
 		}
 	}
-	
+
 	public class AccessInfo {
 		private String name;
 		private String infos;
 		private String dates;
 		private OfferAccess link;
 		private final boolean paymentMethod;
-		
+
 		public AccessInfo(String name, boolean paymentMethod, String infos, OfferAccess link) {
 			this.name = name;
 			this.paymentMethod = paymentMethod;
 			this.infos = infos;
 			this.link = link;
 		}
-		
+
 		public String getName() {
 			return name;
 		}
-		
+
 		public void setName(String name) {
 			this.name = name;
 		}
-		
+
 		public boolean isPaymentMethod() {
 			return paymentMethod;
 		}
@@ -416,7 +416,7 @@ public class AccessConfigurationController extends FormBasicController {
 						BigDecimal vat = acModule.getVat();
 						String vatStr = vat == null ? "" : vat.setScale(3, BigDecimal.ROUND_HALF_EVEN).toPlainString();
 						return translate("access.info.price.vat", new String[]{price, vatStr});
-						
+
 					} else {
 						return translate("access.info.price.noVat", new String[]{price});
 					}
@@ -427,7 +427,7 @@ public class AccessConfigurationController extends FormBasicController {
 			}
 			return "";
 		}
-		
+
 		public void setInfos(String infos) {
 			this.infos = infos;
 		}
diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/AccessControlAdminController.java b/src/main/java/org/olat/resource/accesscontrol/ui/AccessControlAdminController.java
index ff0a9d537765b8bbb8a69a9699b53639d7a9c9ca..65ba7e2112d4e57d75351dabd56a577c72c6018c 100644
--- a/src/main/java/org/olat/resource/accesscontrol/ui/AccessControlAdminController.java
+++ b/src/main/java/org/olat/resource/accesscontrol/ui/AccessControlAdminController.java
@@ -34,53 +34,59 @@ import org.olat.core.gui.control.Controller;
 import org.olat.core.gui.control.WindowControl;
 import org.olat.resource.accesscontrol.AccessControlModule;
 import org.olat.resource.accesscontrol.method.AccessMethodHandler;
+import org.olat.resource.accesscontrol.provider.free.FreeAccessHandler;
+import org.olat.resource.accesscontrol.provider.paypal.PaypalAccessHandler;
+import org.olat.resource.accesscontrol.provider.token.TokenAccessHandler;
 
 /**
- * 
+ *
  * Description:<br>
- * 
+ *
  * <P>
  * Initial Date:  26 mai 2011 <br>
  *
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 public class AccessControlAdminController extends FormBasicController {
-	
+
+	private final static String METHOD_AUTO = "ac.method.auto.name";
 	private MultipleSelectionElement enabled, homeEnabled;
 	private MultipleSelectionElement methods;
-	
+
 	private String[] values = {""};
 	private String[] keys = {"on"};
-	
+
 	private String[] methodValues = {""};
 	private String[] methodKeys = {""};
-	
+
 	private final AccessControlModule acModule;
 
 	public AccessControlAdminController(UserRequest ureq, WindowControl wControl) {
 		super(ureq, wControl);
-		
+
 		acModule = CoreSpringFactory.getImpl(AccessControlModule.class);
 
 		values = new String[] {
 			getTranslator().translate("ac.on")
 		};
-		
+
 		methodKeys = new String[] {
-			"free.method",
-			"token.method",
-			"paypal.method"
+			FreeAccessHandler.METHOD_TYPE,
+			TokenAccessHandler.METHOD_TYPE,
+			PaypalAccessHandler.METHOD_TYPE,
+			METHOD_AUTO
 		};
 
 		methodValues = new String[methodKeys.length];
-		for(int i=0; i<methodKeys.length; i++) {
+		for(int i=0; i<methodKeys.length-1; i++) {
 			AccessMethodHandler handler = acModule.getAccessMethodHandler(methodKeys[i]);
 			methodValues[i] = handler.getMethodName(getLocale());
 		}
-		
+		methodValues[3] = getTranslator().translate(METHOD_AUTO);
+
 		initForm(ureq);
 	}
-	
+
 	@Override
 	protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
 		setFormTitle("admin.title");
@@ -88,32 +94,33 @@ public class AccessControlAdminController extends FormBasicController {
 
 		enabled = uifactory.addCheckboxesHorizontal("ac.enabled", formLayout, keys, values);
 		enabled.select(keys[0], acModule.isEnabled());
-		
+
 		uifactory.addSpacerElement("spaceman", formLayout, false);
-		
+
 		methods = uifactory.addCheckboxesVertical("ac.methods", formLayout, methodKeys, methodValues, 1);
-		methods.select("free.method", acModule.isFreeEnabled());
-		methods.select("token.method", acModule.isTokenEnabled());
-		methods.select("paypal.method", acModule.isPaypalEnabled());
+		methods.select(FreeAccessHandler.METHOD_TYPE, acModule.isFreeEnabled());
+		methods.select(TokenAccessHandler.METHOD_TYPE, acModule.isTokenEnabled());
+		methods.select(PaypalAccessHandler.METHOD_TYPE, acModule.isPaypalEnabled());
+		methods.select(METHOD_AUTO, acModule.isAutoEnabled());
 		methods.setEnabled(acModule.isEnabled());
 		methods.addActionListener(FormEvent.ONCHANGE);
-		
+
 		uifactory.addSpacerElement("itgirl", formLayout, false);
-		
+
 		homeEnabled = uifactory.addCheckboxesHorizontal("ac.home.enabled", formLayout, keys, values);
 		homeEnabled.select(keys[0], acModule.isPaypalEnabled() || acModule.isHomeOverviewEnabled());
-		
+
 		final FormLayoutContainer buttonGroupLayout = FormLayoutContainer.createButtonLayout("buttonLayout", getTranslator());
 		buttonGroupLayout.setRootForm(mainForm);
 		uifactory.addFormSubmitButton("save", buttonGroupLayout);
-		
+
 		formLayout.add(buttonGroupLayout);
 		update();
 	}
-	
+
 	public void update() {
 		Collection<String> selectedMethods = methods.getSelectedKeys();
-		if(selectedMethods.contains("paypal.method")) {
+		if(selectedMethods.contains(PaypalAccessHandler.METHOD_TYPE)) {
 			homeEnabled.select(keys[0], true);
 			homeEnabled.setEnabled(false);
 		} else {
@@ -137,16 +144,17 @@ public class AccessControlAdminController extends FormBasicController {
 	protected void formOK(UserRequest ureq) {
 		boolean on = !enabled.getSelectedKeys().isEmpty();
 		acModule.setEnabled(on);
-		
+
 		Collection<String> selectedMethods = methods.getSelectedKeys();
-		acModule.setFreeEnabled(selectedMethods.contains("free.method"));
-		acModule.setTokenEnabled(selectedMethods.contains("token.method"));
-		boolean paypalEnabled = selectedMethods.contains("paypal.method");
+		acModule.setFreeEnabled(selectedMethods.contains(FreeAccessHandler.METHOD_TYPE));
+		acModule.setTokenEnabled(selectedMethods.contains(TokenAccessHandler.METHOD_TYPE));
+		boolean paypalEnabled = selectedMethods.contains(PaypalAccessHandler.METHOD_TYPE);
 		acModule.setPaypalEnabled(paypalEnabled);
-		
+		acModule.setAutoEnabled(selectedMethods.contains(METHOD_AUTO));
+
 		boolean homeOverviewEnabled = paypalEnabled || !homeEnabled.getSelectedKeys().isEmpty();
 		acModule.setHomeOverviewEnabled(homeOverviewEnabled);
-		
+
 		methods.setEnabled(on);
 		showInfo("ac.saved");
 	}
diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties
index 41fb3bbe72fb650f165d59abbc05d876c4d9adca..f58e5ddd9114976eb36b5540983c4004b117a996 100644
--- a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_de.properties
@@ -4,6 +4,7 @@ ac.from.label=g\u00FCltig von {0}
 ac.fromto.label=g\u00FCltig von {0} bis {1}
 ac.home.enabled=Buchungsliste im Home
 ac.methods=Verf\u00FCgbare Buchungsmethoden
+ac.method.auto.name=Automatische Buchungen
 ac.methods.label=Buchungsmethoden
 ac.on=Ein
 ac.saved=Die Konfiguration der Zugangskontrolle und Buchungsmethoden wurde erfolgreich gespeichert
@@ -11,7 +12,7 @@ ac.to.label=g\u00FCltig bis {0}
 access.button=Buchen
 access.desc=Diese Ressource wurde von Ihnen noch nicht gebucht. Eine Buchung ist notwendig um diese Ressource aufrufen zu k\u00F6nnen. W\u00E4hlen Sie eine der unten aufgef\u00FChren Buchungsmethoden auf um sich zu registrieren.
 access.free.desc=Diese Ressource ist f\u00FCr Sie frei verf\u00FCgbar. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:access.button" um sich f\u00FCr den Zugang zu registrieren.
-access.free.title=Freie Ressource 
+access.free.title=Freie Ressource
 access.info.price.noVat={0}
 access.info.price.vat={0} (inkl. {1}% MwSt)
 access.refused.desc=Diese Ressource kann zur Zeit nicht gebucht werden. Bitte kontaktieren Sie den Besitzer dieser Ressource.
@@ -20,7 +21,7 @@ access.token.desc=Diese Ressource ist mit einem Zugangscode gesch\u00FCtzt. Gebe
 access.token.title=Gesch\u00FCtzte Ressource
 accesscontrol.desc=Sie k\u00F6nnen die Buchungsmethoden f\u00FCr den Zugang zu dieser Ressource konfigurieren. $\:accesscontrol.desc.end
 accesscontrol.desc.end=W\u00E4hlen Sie die Schaltfl\u00E4che "$\:add.accesscontrol" um aus der Liste der verf\u00FCgbaren Buchungsmethoden eine oder mehrere auszuw\u00E4hlen. Optional k\u00F6nnen Buchungsmethoden mit einem G\u00FCltigkeitsdatum versehen werden.
-accesscontrol.no.methods.full.access=Es ist noch keine Buchungsmethode konfiguriert.<br />Der Zugang zu dieser Ressource steht allen Systembenutzern ohne Buchung offen. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:add.accesscontrol" um den Zugang einzuschr\u00E4nken und/oder eine Buchung zu erzwingen. 
+accesscontrol.no.methods.full.access=Es ist noch keine Buchungsmethode konfiguriert.<br />Der Zugang zu dieser Ressource steht allen Systembenutzern ohne Buchung offen. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:add.accesscontrol" um den Zugang einzuschr\u00E4nken und/oder eine Buchung zu erzwingen.
 accesscontrol.no.methods.no.access=Es ist noch keine Buchungsmethode konfiguriert.<br />Diese Ressource kann nur von den Teilnehmer der Ressource verwendet werden. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:add.accesscontrol" um eine Buchungsmethode zu w\u00E4hlen und die Ressource zu ver\u00F6ffentlichen.
 accesscontrol.table.from=g\u00FCltig von
 accesscontrol.table.method=Buchungsmethode
@@ -29,9 +30,9 @@ accesscontrol.title=Buchungsmethoden konfigurieren
 accesscontrol.token=Zugangscode
 accesscontrol_group.desc=Sie k\u00F6nnen die Buchungsmethoden f\u00FCr den Zugang zu dieser Gruppe konfigurieren. $\:accesscontrol.desc.end
 add.accesscontrol=Buchungsmethode hinzuf\u00FCgen
-add.accesscontrol.intro=W\u00E4hlen Sie eine Buchungsmethode f\u00FCr die Zugangskontrolle dieser Ressource. Sie k\u00F6nnen mehrere Buchungsmethoden mit unterschiedlichen Datumseinschr\u00E4nkungen w\u00E4hlen. 
+add.accesscontrol.intro=W\u00E4hlen Sie eine Buchungsmethode f\u00FCr die Zugangskontrolle dieser Ressource. Sie k\u00F6nnen mehrere Buchungsmethoden mit unterschiedlichen Datumseinschr\u00E4nkungen w\u00E4hlen.
 add.token=Zugangscode erstellen
-admin.desc=Hier k\u00F6nnen Sie die Zugangskontrolle f\u00FCr Lernressourcen und Gruppen f\u00FCr das gesamte System ein- und ausschalten. Bei eingeschalteter Zugangskontrolle k\u00F6nnen Sie die zur Verf\u00FCgung stehenden Buchungsmethoden ausw\u00E4hlen. 
+admin.desc=Hier k\u00F6nnen Sie die Zugangskontrolle f\u00FCr Lernressourcen und Gruppen f\u00FCr das gesamte System ein- und ausschalten. Bei eingeschalteter Zugangskontrolle k\u00F6nnen Sie die zur Verf\u00FCgung stehenden Buchungsmethoden ausw\u00E4hlen.
 admin.menu.title=Zugangskontrolle
 admin.menu.title.alt=$\:admin.title
 admin.title=Verwaltung der Zugangskontrolle und Buchungsmethoden
@@ -50,17 +51,17 @@ members.name=Name
 members.owners=Besitzer
 members.owners.alt=Benutzer mit Besitzerrechten f\u00FCr diese Lernressource
 members.owners.confirm.remove=Bitte best\u00E4tigen Sie, dass Sie die Autoren "{0}" aus allen Gruppen austragen wollen\:
-members.owners.info=Diese Liste zeigt die Benutzer mit Besitzerrechten f\u00FCr diese Lernressource. Diese Personen haben vollen Zugang zu allen administrativen Werkzeugen der Ressource inklusive der Verwaltung der Besitzer. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:members.add" oder "$\:members.import" um einem bzw. mehreren Benutzern die Besitzerrechte zuzuteilen. 
+members.owners.info=Diese Liste zeigt die Benutzer mit Besitzerrechten f\u00FCr diese Lernressource. Diese Personen haben vollen Zugang zu allen administrativen Werkzeugen der Ressource inklusive der Verwaltung der Besitzer. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:members.add" oder "$\:members.import" um einem bzw. mehreren Benutzern die Besitzerrechte zuzuteilen.
 members.participants=Teilnehmer
 members.participants.alt=Benutzer mit normalen Teilnehmerfunktionen
 members.participants.confirm.remove=Bitte best\u00E4tigen Sie, dass Sie die Teilnehmer "{0}" aus allen Gruppen austragen wollen\:
-members.participants.info=Diese Liste zeigt die Benutzer mit Teilnehmerrechten f\u00FCr diese Lernressource. Diese Personen haben Zugang zu der Lernressource wenn sie ver\u00F6ffentlicht wurde. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:members.add" oder "$\:members.import" um einem bzw. mehreren Benutzern den Zugang zu gestatten.  <br />Wenn Sie mit Lerngruppen arbeiten, dann werden Teilnehmer von Lerngruppen dieser Lernressource automatisch auch in die Teilnehmerliste eingetragen. <br /> Wenn Sie mit Buchungen arbeiten, dann werden Teilnehmer die diese Lernressource buchen automatisch in diese Teilnehmerliste eingetragen. <br /> Sie k\u00F6nnen eine Person jederzeit aus dieser Liste austragen. Die Person hat anschliessend keinen Zugang mehr zu dieser Lernressource. 
+members.participants.info=Diese Liste zeigt die Benutzer mit Teilnehmerrechten f\u00FCr diese Lernressource. Diese Personen haben Zugang zu der Lernressource wenn sie ver\u00F6ffentlicht wurde. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:members.add" oder "$\:members.import" um einem bzw. mehreren Benutzern den Zugang zu gestatten.  <br />Wenn Sie mit Lerngruppen arbeiten, dann werden Teilnehmer von Lerngruppen dieser Lernressource automatisch auch in die Teilnehmerliste eingetragen. <br /> Wenn Sie mit Buchungen arbeiten, dann werden Teilnehmer die diese Lernressource buchen automatisch in diese Teilnehmerliste eingetragen. <br /> Sie k\u00F6nnen eine Person jederzeit aus dieser Liste austragen. Die Person hat anschliessend keinen Zugang mehr zu dieser Lernressource.
 members.title=Mitgliederverwaltung
 members.title.alt=Verwalten von Besitzern, Betreuern und Teilnehmern dieser Lernressource
 members.tutors=Betreuer
 members.tutors.alt=Benutzer mit Betreuungsfunktionen f\u00FCr diese Lernressource
 members.tutors.confirm.remove=Bitte best\u00E4tigen Sie, dass Sie die Betreuer "{0}" aus allen Gruppen austragen wollen\:
-members.tutors.info=Diese Liste zeigt die Benutzer mit Betreuungsrechten f\u00FCr diese Lernressource. Diese Personen haben Zugang zu der Lernressource wenn sie ver\u00F6ffentlicht wurde analog zu den Teilnehmern. In Kursen stehen den Betreuern Werkzeuge wie z.B. das Bewertungswerkzeug zur Verf\u00FCgung. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:members.add" oder "$\:members.import" um einem bzw. mehreren Benutzern die Betreuungsrechte zuzuteilen. <br />Wenn Sie mit Lerngruppen arbeiten, dann werden Betreuer von Lerngruppen dieser Lernressource automatisch auch in die Liste der Betreuer eingetragen. 
+members.tutors.info=Diese Liste zeigt die Benutzer mit Betreuungsrechten f\u00FCr diese Lernressource. Diese Personen haben Zugang zu der Lernressource wenn sie ver\u00F6ffentlicht wurde analog zu den Teilnehmern. In Kursen stehen den Betreuern Werkzeuge wie z.B. das Bewertungswerkzeug zur Verf\u00FCgung. W\u00E4hlen Sie die Schaltfl\u00E4che "$\:members.add" oder "$\:members.import" um einem bzw. mehreren Benutzern die Betreuungsrechte zuzuteilen. <br />Wenn Sie mit Lerngruppen arbeiten, dann werden Betreuer von Lerngruppen dieser Lernressource automatisch auch in die Liste der Betreuer eingetragen.
 membership.delete.desc=Bitte best\u00E4tigen Sie, dass der gew\u00E4hlte Benutzer aus dieser Liste entfernt werden soll\:
 membership.delete.title=Benutzer entfernen
 menu.orders=Buchungen
diff --git a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties
index 94237c4ff52c204e7c35bcbb3b9ce27a54b3c2c9..baa5865578c01e6aa9987ac84a6d438b77a25ece 100644
--- a/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/resource/accesscontrol/ui/_i18n/LocalStrings_en.properties
@@ -4,6 +4,7 @@ ac.from.label=valid from {0}
 ac.fromto.label=valid from {0} to {1}
 ac.home.enabled=Bookings in Home
 ac.methods=Available booking methods
+ac.method.auto.name=Automatic booking
 ac.methods.label=Booking method
 ac.on=On
 ac.saved=The configuration for access control and booking methods has been saved
@@ -31,7 +32,7 @@ accesscontrol_group.desc=You can configure booking methods for accessing this gr
 add.accesscontrol=Add booking method
 add.accesscontrol.intro=Select a booking method to control access to this resource. You can add multiple booking methods and configure different validation periods for each booking method.
 add.token=Create access code
-admin.desc=Here you can define if the access control mechanism for learning resources and groups should be enabled system wide. If access control is enabled you can then choose the booking methods that should be enabled.  
+admin.desc=Here you can define if the access control mechanism for learning resources and groups should be enabled system wide. If access control is enabled you can then choose the booking methods that should be enabled.
 admin.menu.title=Access control
 admin.menu.title.alt=$\:admin.title
 admin.title=Access control and booking methods administration
diff --git a/src/main/java/org/olat/shibboleth/ShibbolethManager.java b/src/main/java/org/olat/shibboleth/ShibbolethManager.java
index 0b3da5a76e135abf9f97b0a0996df0a31323c605..66b396526119f506c1a1542f43793864517c0de1 100644
--- a/src/main/java/org/olat/shibboleth/ShibbolethManager.java
+++ b/src/main/java/org/olat/shibboleth/ShibbolethManager.java
@@ -35,7 +35,8 @@ public interface ShibbolethManager {
 	 * Create and persist an OpenOLAT user and synchronize the Shibboleth user
 	 * attribute with the OpenOLAT user properties. The new user is added with
 	 * the role user. The new user is added to the role authors if this function
-	 * is enabled. Required Attributes have to be checked before this method.
+	 * is enabled. If the auto access control is enabled the access orders are
+	 * created. Required Attributes have to be checked before this method.
 	 *
 	 * @param username
 	 * @param shibbolethUniqueID
@@ -47,7 +48,9 @@ public interface ShibbolethManager {
 
 	/**
 	 * Synchronize the Shibboleth user attributes to the OpenOLAT user
-	 * properties and persist the user in the database.
+	 * properties and persist the user in the database. The new user is added to
+	 * the role authors if this function is enabled. If the auto access control
+	 * is enabled the access orders are created.
 	 *
 	 * @param identity
 	 * @param shibbolethAttributes
diff --git a/src/main/java/org/olat/shibboleth/ShibbolethModule.java b/src/main/java/org/olat/shibboleth/ShibbolethModule.java
index 31d24f7c10c3520bb3c6d08388e9de7cc22cc102..ea09668763f5a2065107be9d237d330e06428633 100644
--- a/src/main/java/org/olat/shibboleth/ShibbolethModule.java
+++ b/src/main/java/org/olat/shibboleth/ShibbolethModule.java
@@ -26,6 +26,7 @@
 package org.olat.shibboleth;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -41,6 +42,7 @@ import org.olat.core.logging.OLog;
 import org.olat.core.logging.Tracing;
 import org.olat.core.util.StringHelper;
 import org.olat.core.util.coordinate.CoordinatorManager;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
 import org.olat.shibboleth.util.AttributeTranslator;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
@@ -109,6 +111,14 @@ public class ShibbolethModule extends AbstractSpringModule implements ConfigOnOf
 	@Value("${shibboleth.ac.attribute2Values:#{null}}")
 	private String attribute2Values;
 
+	@Value("${method.auto.shib.identifiers}")
+	private String acAutoIdentifiersString;
+	private Set<IdentifierKey> acAutoIdentifiers;
+	@Value("${method.auto.shib.shib}")
+	private String acAutoAttributeName;
+	@Value("${method.auto.shib.splitter}")
+	private String acAutoSplitter;
+
 	@Autowired
 	public ShibbolethModule(CoordinatorManager coordinatorManager) {
 		super(coordinatorManager);
@@ -149,6 +159,21 @@ public class ShibbolethModule extends AbstractSpringModule implements ConfigOnOf
 		if(StringHelper.containsNonWhitespace(attribute2ValuesObj)) {
 			attribute2Values = attribute2ValuesObj;
 		}
+
+		acAutoIdentifiers = parseAcIdentifiers(acAutoIdentifiersString);
+	}
+
+	private Set<IdentifierKey> parseAcIdentifiers(String raw) {
+		Set<IdentifierKey> keys = new HashSet<>();
+		List<String> keyStrings = Arrays.asList(raw.split(","));
+		for (String keyString : keyStrings) {
+			try {
+				keys.add(IdentifierKey.valueOf(keyString));
+			} catch (Exception e) {
+				log.warn("The value '" + keyString + "' for the property 'shib.ac.auto.identifiers' is not valid.");
+			}
+		}
+		return keys;
 	}
 
 	@Override
@@ -331,7 +356,20 @@ public class ShibbolethModule extends AbstractSpringModule implements ConfigOnOf
 		attributeNames.add(authorMappingAttributeName);
 		attributeNames.add(attribute1);
 		attributeNames.add(attribute2);
+		attributeNames.add(acAutoAttributeName);
 		return attributeNames;
 	}
 
+	public Set<IdentifierKey> getAcAutoIdentifiers() {
+		return acAutoIdentifiers;
+	}
+
+	public String getAcAutoAttributeName() {
+		return acAutoAttributeName;
+	}
+
+	public String getAcAutoSplitter() {
+		return acAutoSplitter;
+	}
+
 }
\ No newline at end of file
diff --git a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_de.properties
index a0859513d4288f4354f24b5b586c232d895fea73..e71e3fa2e373cdad9816e123851050945062cfdb 100644
--- a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_de.properties
@@ -11,7 +11,7 @@ admin.menu.shibboleth.desc=Konfiguration Shibboleth Modul
 admin.title=Shibboleth auhorization
 authentication.provider.description=Sind Sie Mitglied einer Institution mit Shibboleth Loginverfahren?
 authentication.provider.linkText=Anmelden mit Shibboleth Konto
-
+auto.shib.method=Shibboleth
 
 
 
diff --git a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_en.properties
index ffa2aacd223963c92445d461a93d7c07c3f6d529..9fce210c9cd92a716ea95a2afa0f1ddcdbc29dc6 100644
--- a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_en.properties
@@ -11,7 +11,7 @@ admin.menu.shibboleth.desc=Configuration Shibboleth module
 admin.title=Shibboleth auhorization
 authentication.provider.description=Are you a member of an institutions that uses Shibboleth authentication?
 authentication.provider.linkText=Login with Shibboleth account
-
+auto.shib.method=Shibboleth
 
 
 
diff --git a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_fr.properties b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_fr.properties
index 8041afdb132180845bf6f9db03dc1f7c33617f83..c6acd8dd021806aa9463685b936f15c537591b30 100644
--- a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_fr.properties
+++ b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_fr.properties
@@ -11,6 +11,7 @@ admin.menu.shibboleth.desc=Configuration du module Shibboleth
 admin.title=Autorisation Shibboleth
 authentication.provider.description=\u00CAtes-vous membre d'une institution qui utilise Shibboleth pour l'authentification?
 authentication.provider.linkText=Se connecter avec un compte Shibboleth
+auto.shib.method=Shibboleth
 eduPersonAffiliation=Affiliation
 eduPersonAffiliation.affiliate=Affili\u00E9s
 eduPersonAffiliation.alum=Anciens
diff --git a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_it.properties b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_it.properties
index 632235101a045bfede1bf730a1975471d2600838..1e59b0b448934da6441010017138e6673b3b8afa 100644
--- a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_it.properties
+++ b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_it.properties
@@ -11,6 +11,7 @@ admin.menu.shibboleth.desc=Configurazione modulo Shibboleth
 admin.title=Autorizzazione Shibboleth
 authentication.provider.description=Sei un membro di una istituzione che utilizza Single Sign-On basato su autenticazione Shibboleth?
 authentication.provider.linkText=Login con account Shibboleth
+auto.shib.method=Shibboleth
 eduPersonAffiliation=Affiliazione
 eduPersonAffiliation.affiliate=Affiliati
 eduPersonAffiliation.alum=Alumni
diff --git a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_nl_NL.properties b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_nl_NL.properties
index 605ede64135cfadb4b914a0e153b3b6490accaf2..77d64188e1d7212cc9cab5a8b9e8d702d824cbb9 100644
--- a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_nl_NL.properties
+++ b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_nl_NL.properties
@@ -9,6 +9,7 @@
 
 
 
+auto.shib.method=Shibboleth
 eduPersonAffiliation=Aansluiting
 eduPersonAffiliation.affiliate=Aansluiten
 eduPersonAffiliation.alum=Voorafbestaande
diff --git a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pl.properties b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pl.properties
index b61a4aab5652f6c1b6f5adcfe942ba40e7c8ac82..83b88e93a0c35afbf89355baa32f2449abb0b98a 100644
--- a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pl.properties
+++ b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pl.properties
@@ -8,7 +8,7 @@
 
 
 
-
+auto.shib.method=Shibboleth
 eduPersonAffiliation=Affiliation
 eduPersonAffiliation.affiliate=Affiliate
 eduPersonAffiliation.alum=Alumni
diff --git a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pt_BR.properties b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pt_BR.properties
index 48acfb80fa5e940b36b2303ebd6953224d289219..4bb268bcc9f1f45d93810eae41664961501007b0 100644
--- a/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pt_BR.properties
+++ b/src/main/java/org/olat/shibboleth/_i18n/LocalStrings_pt_BR.properties
@@ -11,6 +11,7 @@ admin.menu.shibboleth.desc=M\u00F3dulo de configura\u00E7\u00E3o Shibboleth
 admin.title=Autoriza\u00E7\u00E3o Shibboleth
 authentication.provider.description=Voc\u00EA faz parte de uma das institui\u00E7\u00F5es que utilizam autentica\u00E7\u00E3o Shibboleth?
 authentication.provider.linkText=Entrar com conta Shibboleth
+auto.shib.method=Shibboleth
 eduPersonAffiliation=Filia\u00E7\u00E3o
 eduPersonAffiliation.affiliate=Filiado
 eduPersonAffiliation.alum=Alumni
diff --git a/src/main/java/org/olat/shibboleth/manager/ShibbolethAdvanceOrderInput.java b/src/main/java/org/olat/shibboleth/manager/ShibbolethAdvanceOrderInput.java
new file mode 100644
index 0000000000000000000000000000000000000000..cd5a363a1c9466468dad966f1f0469b113f3e76b
--- /dev/null
+++ b/src/main/java/org/olat/shibboleth/manager/ShibbolethAdvanceOrderInput.java
@@ -0,0 +1,82 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.shibboleth.manager;
+
+import java.util.Set;
+
+import org.olat.core.id.Identity;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrderInput;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.olat.resource.accesscontrol.provider.auto.model.AutoAccessMethod;
+import org.olat.shibboleth.ShibbolethModule;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Initial date: 17.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+@Scope("prototype")
+public class ShibbolethAdvanceOrderInput implements AdvanceOrderInput {
+
+	@Autowired
+	private ShibbolethModule shibbolethModule;
+
+	private Identity identity;
+	private String rawValues;
+
+	@Override
+	public Class<? extends AutoAccessMethod> getMethodClass() {
+		return ShibbolethAutoAccessMethod.class;
+	}
+
+	public void setIdentity(Identity identity) {
+		this.identity = identity;
+	}
+
+	@Override
+	public Identity getIdentity() {
+		return identity;
+	}
+
+	@Override
+	public Set<IdentifierKey> getKeys() {
+		return shibbolethModule.getAcAutoIdentifiers();
+	}
+
+	public void setRawValues(String rawValues) {
+		this.rawValues = rawValues;
+	}
+
+	@Override
+	public String getRawValues() {
+		return rawValues;
+	}
+
+	@Override
+	public String getSplitterType() {
+		return shibbolethModule.getAcAutoSplitter();
+	}
+
+}
diff --git a/src/main/java/org/olat/shibboleth/manager/ShibbolethAttributes.java b/src/main/java/org/olat/shibboleth/manager/ShibbolethAttributes.java
index 0c4446614fa2fea2e77c8165a2e4bccde971225e..eab0af31aec0a3b8adf304da1dda9db94a0b10aa 100644
--- a/src/main/java/org/olat/shibboleth/manager/ShibbolethAttributes.java
+++ b/src/main/java/org/olat/shibboleth/manager/ShibbolethAttributes.java
@@ -82,6 +82,11 @@ public class ShibbolethAttributes {
 		return getValueForAttributeName(langAttributeName);
 	}
 
+	public String getAcRawValues() {
+		String langAttributeName = shibbolethModule.getAcAutoAttributeName();
+		return getValueForAttributeName(langAttributeName);
+	}
+
 	public String getValueForAttributeName(String attributeName) {
 		return shibbolethMap.get(attributeName);
 	}
diff --git a/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessHandler.java b/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..81d39a87a774d2224453a24da96258dd58c2cbfc
--- /dev/null
+++ b/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessHandler.java
@@ -0,0 +1,50 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.shibboleth.manager;
+
+import java.util.Locale;
+
+import org.olat.core.gui.translator.Translator;
+import org.olat.core.util.Util;
+import org.olat.resource.accesscontrol.provider.auto.AutoAccessHandler;
+import org.olat.shibboleth.ShibbolethModule;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ * Initial date: 17.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Component
+public class ShibbolethAutoAccessHandler extends AutoAccessHandler {
+	public static final String METHOD_TYPE = "auto.shib.method";
+
+	@Override
+	public String getType() {
+		return METHOD_TYPE;
+	}
+
+	@Override
+	public String getMethodName(Locale locale) {
+		Translator translator = Util.createPackageTranslator(ShibbolethModule.class, locale);
+		return translator.translate(METHOD_TYPE);
+	}
+}
diff --git a/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessMethod.java b/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessMethod.java
new file mode 100644
index 0000000000000000000000000000000000000000..1de0debf5d7fc177732fba0495b680c196b602aa
--- /dev/null
+++ b/src/main/java/org/olat/shibboleth/manager/ShibbolethAutoAccessMethod.java
@@ -0,0 +1,45 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.shibboleth.manager;
+
+import javax.persistence.DiscriminatorValue;
+import javax.persistence.Entity;
+
+import org.olat.resource.accesscontrol.provider.auto.model.AutoAccessMethod;
+
+/**
+ *
+ * Initial date: 17.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+@Entity(name="acautomethod")
+@DiscriminatorValue(value="auto.shib.method")
+public class ShibbolethAutoAccessMethod extends AutoAccessMethod {
+
+	private static final long serialVersionUID = -241494885573765862L;
+
+	@Override
+	public String getType() {
+		return ShibbolethAutoAccessHandler.METHOD_TYPE;
+	}
+
+
+}
diff --git a/src/main/java/org/olat/shibboleth/manager/ShibbolethManagerImpl.java b/src/main/java/org/olat/shibboleth/manager/ShibbolethManagerImpl.java
index f29e4fb83e035ebb4fbf54530a39206c73ee66f6..ab78d055a7b82d5698a6369c9e401a58513e029b 100644
--- a/src/main/java/org/olat/shibboleth/manager/ShibbolethManagerImpl.java
+++ b/src/main/java/org/olat/shibboleth/manager/ShibbolethManagerImpl.java
@@ -23,10 +23,14 @@ import org.olat.basesecurity.BaseSecurity;
 import org.olat.basesecurity.BaseSecurityManager;
 import org.olat.basesecurity.Constants;
 import org.olat.basesecurity.SecurityGroup;
+import org.olat.core.CoreSpringFactory;
 import org.olat.core.id.Identity;
 import org.olat.core.id.User;
+import org.olat.resource.accesscontrol.AccessControlModule;
+import org.olat.resource.accesscontrol.provider.auto.AutoAccessManager;
 import org.olat.shibboleth.ShibbolethDispatcher;
 import org.olat.shibboleth.ShibbolethManager;
+import org.olat.shibboleth.ShibbolethModule;
 import org.olat.user.UserManager;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -42,8 +46,14 @@ public class ShibbolethManagerImpl implements ShibbolethManager {
 
 	private BaseSecurity securityManager;
 
+	@Autowired
+	private ShibbolethModule shibbolethModule;
+	@Autowired
+	private AccessControlModule acModule;
 	@Autowired
 	private UserManager userManager;
+	@Autowired
+	private AutoAccessManager autoAccessManager;
 
 	public ShibbolethManagerImpl() {
 		securityManager = BaseSecurityManager.getInstance();
@@ -56,6 +66,7 @@ public class ShibbolethManagerImpl implements ShibbolethManager {
 		Identity identity = createUserAndPersist(username, shibbolethUniqueID, language, shibbolethAttributes);
 		addToUsersGroup(identity);
 		addToAuthorsGroup(identity, shibbolethAttributes);
+		createAndBookAdvanceOrders(identity, shibbolethAttributes);
 
 		return identity;
 	}
@@ -89,6 +100,21 @@ public class ShibbolethManagerImpl implements ShibbolethManager {
 		return securityManager.findSecurityGroupByName(Constants.GROUP_AUTHORS);
 	}
 
+	private void createAndBookAdvanceOrders(Identity identity, ShibbolethAttributes shibbolethAttributes) {
+		if (acModule.isAutoEnabled()) {
+			createAdvanceOr(identity, shibbolethAttributes);
+			autoAccessManager.grantAccessToCourse(identity);
+		}
+	}
+
+	private void createAdvanceOr(Identity identity, ShibbolethAttributes shibbolethAttributes) {
+		ShibbolethAdvanceOrderInput input = getShibbolethAdvanceOrderInput();
+		input.setIdentity(identity);
+		String rawValues = shibbolethAttributes.getAcRawValues();
+		input.setRawValues(rawValues);
+		autoAccessManager.createAdvanceOrders(input);
+	}
+
 	@Override
 	public void syncUser(Identity identity, ShibbolethAttributes shibbolethAttributes) {
 		if (identity == null || shibbolethAttributes == null) {
@@ -98,6 +124,7 @@ public class ShibbolethManagerImpl implements ShibbolethManager {
 		User user = identity.getUser();
 		syncAndPersistUser(user, shibbolethAttributes);
 		addToAuthorsGroup(identity, shibbolethAttributes);
+		createAndBookAdvanceOrders(identity, shibbolethAttributes);
 	}
 
 	private void syncAndPersistUser(User user, ShibbolethAttributes shibbolethAttributes) {
@@ -107,4 +134,11 @@ public class ShibbolethManagerImpl implements ShibbolethManager {
 		}
 	}
 
+	/**
+	 * Because the static method of the CoreSpringFactory can not be mocked.
+	 */
+	protected ShibbolethAdvanceOrderInput getShibbolethAdvanceOrderInput() {
+		return CoreSpringFactory.getImpl(ShibbolethAdvanceOrderInput.class);
+	}
+
 }
diff --git a/src/main/resources/META-INF/persistence.xml b/src/main/resources/META-INF/persistence.xml
index 9e839a67c671f02060f9e327cfeab0d63aa0dc94..b82a337e9e482d855fb4a93274a0b39cd4f8cf9e 100644
--- a/src/main/resources/META-INF/persistence.xml
+++ b/src/main/resources/META-INF/persistence.xml
@@ -48,7 +48,7 @@
 		<mapping-file>org/olat/core/util/mail/model/DBMailRecipient.hbm.xml</mapping-file>
 		<mapping-file>org/olat/core/logging/activity/LoggingObject.hbm.xml</mapping-file>
 		<mapping-file>org/olat/course/db/impl/CourseDBEntryImpl.hbm.xml</mapping-file>
-		
+
 		<!-- Start upgraders mapping -->
 		<mapping-file>org/olat/upgrade/model/BGAreaUpgrade.hbm.xml</mapping-file>
 		<mapping-file>org/olat/upgrade/model/BGContextImpl.hbm.xml</mapping-file>
@@ -63,8 +63,8 @@
 		<class>org.olat.upgrade.model.EPMapUpgradeToGroupRelation</class>
 		<class>org.olat.upgrade.model.InvitationUpgrade</class>
 		<!-- End upgraders mapping -->
-		
-		
+
+
 		<class>org.olat.basesecurity.IdentityShort</class>
 		<class>org.olat.basesecurity.model.GroupImpl</class>
 		<class>org.olat.basesecurity.model.GrantImpl</class>
@@ -134,6 +134,7 @@
 		<class>org.olat.resource.accesscontrol.model.FreeAccessMethod</class>
 		<class>org.olat.resource.accesscontrol.model.TokenAccessMethod</class>
 		<class>org.olat.resource.accesscontrol.model.ResourceReservationImpl</class>
+		<class>org.olat.resource.accesscontrol.provider.auto.model.AdvanceOrderImpl</class>
 		<class>org.olat.resource.accesscontrol.provider.paypal.model.PaypalAccessMethod</class>
 		<class>org.olat.instantMessaging.model.InstantMessageImpl</class>
 		<class>org.olat.instantMessaging.model.ImPreferencesImpl</class>
@@ -206,6 +207,7 @@
 		<class>org.olat.ims.lti.model.LTIOutcomeImpl</class>
 		<class>org.olat.portfolio.model.InvitationImpl</class>
 		<class>org.olat.portfolio.model.structel.EPStructureElementToGroupRelation</class>
+		<class>org.olat.shibboleth.manager.ShibbolethAutoAccessMethod</class>
 		<class>org.olat.user.UserImpl</class>
 		<properties>
 			<property name="hibernate.generate_statistics" value="true"/>
diff --git a/src/main/resources/database/mysql/alter_12_0_x_to_12_1_0.sql b/src/main/resources/database/mysql/alter_12_0_x_to_12_1_0.sql
new file mode 100644
index 0000000000000000000000000000000000000000..27b5413ef47cd95bd36da6fc9f58d2be149c305c
--- /dev/null
+++ b/src/main/resources/database/mysql/alter_12_0_x_to_12_1_0.sql
@@ -0,0 +1,20 @@
+-- auto access controll
+create table o_ac_auto_advance_order (
+  id bigint not null auto_increment,
+  creationdate datetime not null,
+  lastmodified datetime not null,
+  a_identifier_key varchar(64) not null,
+  a_identifier_value varchar(64) not null,
+  a_status varchar(32) not null,
+  a_status_modified datetime not null,
+  fk_identity int8 not null,
+  fk_method int8 not null,
+  primary key (id)
+);
+
+alter table o_ac_auto_advance_order ENGINE = InnoDB;
+
+create index idx_ac_aao_id_idx on o_ac_auto_advance_order(id);
+create index idx_ac_aao_identifier_idx on o_ac_auto_advance_order(a_identifier_key, a_identifier_value);
+create index idx_ac_aao_ident_idx on o_ac_auto_advance_order(fk_identity);
+alter table o_ac_auto_advance_order add constraint aao_ident_idx foreign key (fk_identity) references o_bs_identity (id);
\ No newline at end of file
diff --git a/src/main/resources/database/mysql/setupDatabase.sql b/src/main/resources/database/mysql/setupDatabase.sql
index 016db17665df26b81318a11886c080546886cdd9..18c7c57e5879b40e2bdada22c1f9756075c187f1 100644
--- a/src/main/resources/database/mysql/setupDatabase.sql
+++ b/src/main/resources/database/mysql/setupDatabase.sql
@@ -1005,6 +1005,19 @@ create table if not exists o_ac_offer_access (
 	primary key (offer_method_id)
 );
 
+create table o_ac_auto_advance_order (
+  id bigint not null auto_increment,
+  creationdate datetime not null,
+  lastmodified datetime not null,
+  a_identifier_key varchar(64) not null,
+  a_identifier_value varchar(64) not null,
+  a_status varchar(32) not null,
+  a_status_modified datetime not null,
+  fk_identity int8 not null,
+  fk_method int8 not null,
+  primary key (id)
+);
+
 -- access cart
 create table if not exists o_ac_order (
 	order_id bigint NOT NULL,
@@ -2388,6 +2401,7 @@ alter table o_ac_order_line ENGINE = InnoDB;
 alter table o_ac_transaction ENGINE = InnoDB;
 alter table o_ac_reservation ENGINE = InnoDB;
 alter table o_ac_paypal_transaction ENGINE = InnoDB;
+alter table o_ac_auto_advance_order ENGINE = InnoDB;
 alter table o_as_eff_statement ENGINE = InnoDB;
 alter table o_as_user_course_infos ENGINE = InnoDB;
 alter table o_as_mode_course ENGINE = InnoDB;
@@ -2617,6 +2631,11 @@ create index paypal_pay_key_idx on o_ac_paypal_transaction (pay_key);
 create index paypal_pay_trx_id_idx on o_ac_paypal_transaction (ipn_transaction_id);
 create index paypal_pay_s_trx_id_idx on o_ac_paypal_transaction (ipn_sender_transaction_id);
 
+create index idx_ac_aao_id_idx on o_ac_auto_advance_order(id);
+create index idx_ac_aao_identifier_idx on o_ac_auto_advance_order(a_identifier_key, a_identifier_value);
+create index idx_ac_aao_ident_idx on o_ac_auto_advance_order(fk_identity);
+alter table o_ac_auto_advance_order add constraint aao_ident_idx foreign key (fk_identity) references o_bs_identity (id);
+
 -- reservations
 alter table o_ac_reservation add constraint idx_rsrv_to_rsrc_rsrc foreign key (fk_resource) references o_olatresource (resource_id);
 alter table o_ac_reservation add constraint idx_rsrv_to_rsrc_identity foreign key (fk_identity) references o_bs_identity (id);
diff --git a/src/main/resources/database/oracle/alter_12_0_x_to_12_1_0.sql b/src/main/resources/database/oracle/alter_12_0_x_to_12_1_0.sql
new file mode 100644
index 0000000000000000000000000000000000000000..187b7abd4af9f1b727ff7fd7de62bc0fa8c29962
--- /dev/null
+++ b/src/main/resources/database/oracle/alter_12_0_x_to_12_1_0.sql
@@ -0,0 +1,18 @@
+-- auto access controll
+create table o_ac_auto_advance_order (
+  id number(20) generated always as identity,
+  creationdate date not null,
+  lastmodified date not null,
+  a_identifier_key varchar(64) not null,
+  a_identifier_value varchar(64) not null,
+  a_status varchar(32) not null,
+  a_status_modified date not null,
+  fk_identity number(20) not null,
+  fk_method number(20) not null,
+  primary key (id)
+);
+
+create index idx_ac_aao_id_idx on o_ac_auto_advance_order(id);
+create index idx_ac_aao_identifier_idx on o_ac_auto_advance_order(a_identifier_key, a_identifier_value);
+create index idx_ac_aao_ident_idx on o_ac_auto_advance_order(fk_identity);
+alter table o_ac_auto_advance_order add constraint aao_ident_idx foreign key (fk_identity) references o_bs_identity (id);
\ No newline at end of file
diff --git a/src/main/resources/database/oracle/setupDatabase.sql b/src/main/resources/database/oracle/setupDatabase.sql
index 95cdca54ad97ddd38a5aec795de2fcf5b1e1e056..163daab51053437bfc99e0fb430181ef6fa773cf 100644
--- a/src/main/resources/database/oracle/setupDatabase.sql
+++ b/src/main/resources/database/oracle/setupDatabase.sql
@@ -324,7 +324,7 @@ CREATE TABLE o_user (
    u_genericcheckboxproperty varchar2(255 char),
    u_genericcheckboxproperty2 varchar2(255 char),
    u_genericcheckboxproperty3 varchar2(255 char),
-   
+
    fk_identity number(20),
    PRIMARY KEY (user_id)
 );
@@ -759,7 +759,7 @@ create table o_ep_struct_el (
   target_resid number(20),
   target_ressubpath varchar(2048 char),
   target_businesspath varchar(2048 char),
-  style varchar(128 char),  
+  style varchar(128 char),
   status varchar(32 char),
   viewmode varchar(32 char),
   fk_struct_root_id number(20),
@@ -767,7 +767,7 @@ create table o_ep_struct_el (
   fk_map_source_id number(20),
   fk_ownergroup number(20),
   fk_olatresource number(20) not null,
-  primary key (structure_id)  
+  primary key (structure_id)
 );
 
 create table o_ep_struct_struct_link (
@@ -962,7 +962,7 @@ create table o_ac_order_line (
   fk_order_part_id number(20),
   fk_offer_id number(20),
   primary key (order_item_id)
-); 
+);
 
 create table o_ac_transaction (
   transaction_id number(20) NOT NULL,
@@ -1020,6 +1020,19 @@ create table o_ac_paypal_transaction (
    primary key (transaction_id)
 );
 
+create table o_ac_auto_advance_order (
+  id number(20) generated always as identity,
+  creationdate date not null,
+  lastmodified date not null,
+  a_identifier_key varchar(64) not null,
+  a_identifier_value varchar(64) not null,
+  a_status varchar(32) not null,
+  a_status_modified date not null,
+  fk_identity number(20) not null,
+  fk_method number(20) not null,
+  primary key (id)
+);
+
 CREATE TABLE o_stat_lastupdated (
   lastupdated date not null,
   from_datetime date not null,
@@ -1997,13 +2010,13 @@ create table o_sms_message_log (
 );
 
 -- webfeed
-create table o_feed ( 
+create table o_feed (
    id number(20) generated always as identity,
    creationdate date not null,
    lastmodified date not null,
    f_resourceable_id number(20),
    f_resourceable_type varchar(64),
-   f_title varchar(1024), 
+   f_title varchar(1024),
    f_description varchar(1024),
    f_author varchar(255),
    f_image_name varchar(255),
@@ -2218,7 +2231,7 @@ create or replace view o_ep_notifications_rating_v as (
       page.title as page_title,
       urating.creator_id as author_id,
       urating.creationdate as creation_date,
-      urating.lastmodified as last_modified 
+      urating.lastmodified as last_modified
    from o_userrating urating
    inner join o_olatresource rating_resource on (rating_resource.resid = urating.resid and rating_resource.resname = urating.resname)
    inner join o_ep_struct_el map on (map.fk_olatresource = rating_resource.resource_id)
@@ -2242,7 +2255,7 @@ create or replace view o_ep_notifications_comment_v as (
 );
 
 create view o_gp_business_to_repository_v as (
-	select 
+	select
 		grp.group_id as grp_id,
 		repoentry.repositoryentry_id as re_id,
 		repoentry.displayname as re_displayname
@@ -2273,7 +2286,7 @@ create or replace view o_re_membership_v as (
       re.repositoryentry_id as fk_entry_id
    from o_repositoryentry re
    inner join o_re_to_group relgroup on (relgroup.fk_entry_id=re.repositoryentry_id and relgroup.r_defgroup=1)
-   inner join o_bs_group_member bmember on (bmember.fk_group_id=relgroup.fk_group_id) 
+   inner join o_bs_group_member bmember on (bmember.fk_group_id=relgroup.fk_group_id)
 );
 
 -- contacts
@@ -2675,6 +2688,11 @@ create index paypal_pay_key_idx on o_ac_paypal_transaction (pay_key);
 create index paypal_pay_trx_id_idx on o_ac_paypal_transaction (ipn_transaction_id);
 create index paypal_pay_s_trx_id_idx on o_ac_paypal_transaction (ipn_sender_transaction_id);
 
+create index idx_ac_aao_id_idx on o_ac_auto_advance_order(id);
+create index idx_ac_aao_identifier_idx on o_ac_auto_advance_order(a_identifier_key, a_identifier_value);
+create index idx_ac_aao_ident_idx on o_ac_auto_advance_order(fk_identity);
+alter table o_ac_auto_advance_order add constraint aao_ident_idx foreign key (fk_identity) references o_bs_identity (id);
+
 -- reservations
 alter table o_ac_reservation add constraint idx_rsrv_to_rsrc_rsrc foreign key (fk_resource) references o_olatresource (resource_id);
 create index idx_rsrv_to_rsrc_idx on o_ac_reservation(fk_resource);
diff --git a/src/main/resources/database/postgresql/alter_12_0_x_to12_1_0.sql b/src/main/resources/database/postgresql/alter_12_0_x_to12_1_0.sql
new file mode 100644
index 0000000000000000000000000000000000000000..77c3f7ac017959d85337312162a84a69f030e5a2
--- /dev/null
+++ b/src/main/resources/database/postgresql/alter_12_0_x_to12_1_0.sql
@@ -0,0 +1,18 @@
+-- auto access controll
+create table o_ac_auto_advance_order (
+  id bigserial,
+  creationdate timestamp not null,
+  lastmodified timestamp not null,
+  a_identifier_key varchar(64) not null,
+  a_identifier_value varchar(64) not null,
+  a_status varchar(32) not null,
+  a_status_modified timestamp not null,
+  fk_identity int8 not null,
+  fk_method int8 not null,
+  primary key (id)
+);
+
+create index idx_ac_aao_id_idx on o_ac_auto_advance_order(id);
+create index idx_ac_aao_identifier_idx on o_ac_auto_advance_order(a_identifier_key, a_identifier_value);
+create index idx_ac_aao_ident_idx on o_ac_auto_advance_order(fk_identity);
+alter table o_ac_auto_advance_order add constraint aao_ident_idx foreign key (fk_identity) references o_bs_identity (id);
\ No newline at end of file
diff --git a/src/main/resources/database/postgresql/setupDatabase.sql b/src/main/resources/database/postgresql/setupDatabase.sql
index db5e68165cd2cfbfdc940bcdcefa689373cddec2..0a8c631288bbf8a3221ff643a996ceeb338b5028 100644
--- a/src/main/resources/database/postgresql/setupDatabase.sql
+++ b/src/main/resources/database/postgresql/setupDatabase.sql
@@ -869,6 +869,19 @@ create table o_ac_offer_access (
 	primary key (offer_method_id)
 );
 
+create table o_ac_auto_advance_order (
+  id bigserial,
+  creationdate timestamp not null,
+  lastmodified timestamp not null,
+  a_identifier_key varchar(64) not null,
+  a_identifier_value varchar(64) not null,
+  a_status varchar(32) not null,
+  a_status_modified timestamp not null,
+  fk_identity int8 not null,
+  fk_method int8 not null,
+  primary key (id)
+);
+
 -- access cart
 create table o_ac_order (
 	order_id int8 NOT NULL,
@@ -2524,6 +2537,11 @@ create index paypal_pay_key_idx on o_ac_paypal_transaction (pay_key);
 create index paypal_pay_trx_id_idx on o_ac_paypal_transaction (ipn_transaction_id);
 create index paypal_pay_s_trx_id_idx on o_ac_paypal_transaction (ipn_sender_transaction_id);
 
+create index idx_ac_aao_id_idx on o_ac_auto_advance_order(id);
+create index idx_ac_aao_identifier_idx on o_ac_auto_advance_order(a_identifier_key, a_identifier_value);
+create index idx_ac_aao_ident_idx on o_ac_auto_advance_order(fk_identity);
+alter table o_ac_auto_advance_order add constraint aao_ident_idx foreign key (fk_identity) references o_bs_identity (id);
+
 -- reservations
 alter table o_ac_reservation add constraint idx_rsrv_to_rsrc_rsrc foreign key (fk_resource) references o_olatresource (resource_id);
 create index idx_rsrv_to_rsrc_idx on o_ac_reservation(fk_resource);
diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties
index e368f410b88a4a4236c312e8c3416e17ff320363..4c55e193c69c39ecd8a375cefb04507b7e6ee207 100644
--- a/src/main/resources/serviceconfig/olat.properties
+++ b/src/main/resources/serviceconfig/olat.properties
@@ -1164,6 +1164,17 @@ resource.accesscontrol.home.overview=true
 method.token.enabled=true
 method.free.enabled=true
 method.paypal.enabled=false
+method.auto.enabled=false
+
+# Properties to configure the Shibboleth implementation of the auto access method
+# attributes to use for search (comma separated)
+method.auto.shib.identifiers.values=internalId,externalId,externalRef
+method.auto.shib.identifiers=
+# Name of the Shibboleth attributes with the keys
+method.auto.shib.shib=
+# Splitter to split the parsed value in keys
+method.auto.shib.splitter.values=Semicolon
+method.auto.shib.splitter=
 
 ########################################
 # Paypal (need a business account)
diff --git a/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java b/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java
index 2017031211073bcd5fcb01ee0f3cc7bf1120cf52..aaeea9ae542f5bc26005a48f1e8d1a535a0bcd8d 100644
--- a/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java
+++ b/src/test/java/org/olat/group/test/BusinessGroupDAOTest.java
@@ -48,29 +48,35 @@ import org.olat.group.model.BusinessGroupMembershipViewImpl;
 import org.olat.group.model.BusinessGroupQueryParams;
 import org.olat.group.model.BusinessGroupRow;
 import org.olat.group.model.OpenBusinessGroupRow;
-import org.olat.group.model.StatisticsBusinessGroupRow;
 import org.olat.group.model.SearchBusinessGroupParams;
+import org.olat.group.model.StatisticsBusinessGroupRow;
 import org.olat.repository.RepositoryEntry;
 import org.olat.repository.manager.RepositoryEntryRelationDAO;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceManager;
 import org.olat.resource.accesscontrol.ACService;
 import org.olat.resource.accesscontrol.Offer;
+import org.olat.resource.accesscontrol.OfferAccess;
+import org.olat.resource.accesscontrol.manager.ACMethodDAO;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.model.TokenAccessMethod;
 import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
- * 
+ *
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 public class BusinessGroupDAOTest extends OlatTestCase {
-	
+
 	@Autowired
 	private DB dbInstance;
 	@Autowired
 	private ACService acService;
 	@Autowired
+	private ACMethodDAO acMethodManager;
+	@Autowired
 	private MarkManager markManager;
 	@Autowired
 	private BusinessGroupDAO businessGroupDao;
@@ -80,12 +86,12 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 	private BusinessGroupRelationDAO businessGroupRelationDao;
 	@Autowired
 	private RepositoryEntryRelationDAO repositoryEntryRelationDao;
-	
+
 	@Test
 	public void should_service_present() {
 		Assert.assertNotNull(businessGroupDao);
 	}
-	
+
 	@Test
 	public void createBusinessGroup() {
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "gdao", "gdao-desc", -1, -1, false, false, false, false, false);
@@ -103,14 +109,14 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertFalse(group.getWaitingListEnabled());
 		Assert.assertFalse(group.getAutoCloseRanksEnabled());
 	}
-	
+
 	@Test
 	public void loadBusinessGroupStandard() {
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "gdbo", "gdbo-desc", -1, -1, false, false, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		BusinessGroup reloadedGroup = businessGroupDao.load(group.getKey());
-		
+
 		Assert.assertNotNull(reloadedGroup);
 		Assert.assertNull(reloadedGroup.getMinParticipants());
 		Assert.assertNull(reloadedGroup.getMaxParticipants());
@@ -123,13 +129,13 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertFalse(reloadedGroup.getWaitingListEnabled());
 		Assert.assertFalse(reloadedGroup.getAutoCloseRanksEnabled());
 	}
-	
+
 	@Test
 	public void loadBusinessGroup() {
 		//create business group
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "gdco", "gdco-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		BusinessGroup reloadedGroup = businessGroupDao.load(group.getKey());
 		//check the saved values
 		Assert.assertNotNull(reloadedGroup);
@@ -145,17 +151,17 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(reloadedGroup.getWaitingListEnabled());
 		Assert.assertTrue(reloadedGroup.getAutoCloseRanksEnabled());
 	}
-	
+
 	@Test
 	public void loadBusinessGroup_fetch() {
 		//create business group
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "gd-fetch", "gd-fetch-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		BusinessGroup reloadedGroup = businessGroupDao.load(group.getKey());
 		Assert.assertNotNull(reloadedGroup);
 		dbInstance.commitAndCloseSession();
-		
+
 		//check lazy
 		Group baseGroup = reloadedGroup.getBaseGroup();
 		Assert.assertNotNull(baseGroup);
@@ -164,20 +170,20 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(resource);
 		Assert.assertNotNull(resource.getKey());
 	}
-	
+
 	@Test
 	public void loadBusinessGroup_forUpdate() {
 		//create a group
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "gdco", "gdco-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//load an lock
 		BusinessGroup groupForUpdate = businessGroupDao.loadForUpdate(group.getKey());
 		Assert.assertNotNull(groupForUpdate);
 		Assert.assertEquals(group, groupForUpdate);
 		dbInstance.commit();//release lock
 	}
-	
+
 	@Test
 	public void loadBusinessGroup_forUpdate_notFound() {
 		//load and lock an inexistent group
@@ -185,40 +191,40 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNull(groupForUpdate);
 		dbInstance.commit();//release lock
 	}
-	
+
 	@Test
 	public void loadBusinessGroupWithOwner() {
 		Identity owner = JunitTestHelper.createAndPersistIdentityAsUser("bdao-1-" + UUID.randomUUID().toString());
 		dbInstance.commitAndCloseSession();
-		
+
 		BusinessGroup group = businessGroupDao.createAndPersist(owner, "gddo", "gddo-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		BusinessGroup reloadedGroup = businessGroupDao.load(group.getKey());
 		//check if the owner is in the owner security group
 		Assert.assertNotNull(reloadedGroup);
 		boolean isOwner = businessGroupRelationDao.hasRole(owner, reloadedGroup, GroupRoles.coach.name());
 		Assert.assertTrue(isOwner);
 	}
-	
+
 	@Test
 	public void loadBusinessGroupsByIds() {
 		Identity owner = JunitTestHelper.createAndPersistIdentityAsUser("bdao-2-" + UUID.randomUUID().toString());
 		BusinessGroup group1 = businessGroupDao.createAndPersist(owner, "gdeo", "gdeo-desc", 0, 10, true, true, false, false, false);
 		BusinessGroup group2 = businessGroupDao.createAndPersist(owner, "gdfo", "gdfo-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//check if the method is robust against empty list fo keys
 		List<BusinessGroup> groups1 = businessGroupDao.load(Collections.<Long>emptyList());
 		Assert.assertNotNull(groups1);
 		Assert.assertEquals(0, groups1.size());
-		
+
 		//check load 1 group
 		List<BusinessGroup> groups2 = businessGroupDao.load(Collections.singletonList(group1.getKey()));
 		Assert.assertNotNull(groups2);
 		Assert.assertEquals(1, groups2.size());
 		Assert.assertEquals(group1, groups2.get(0));
-		
+
 		//check load 2 groups
 		List<Long> groupKeys = new ArrayList<Long>(2);
 		groupKeys.add(group1.getKey());
@@ -229,25 +235,25 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(groups3.contains(group1));
 		Assert.assertTrue(groups3.contains(group2));
 	}
-	
+
 	@Test
 	public void loadShortBusinessGroupsByKeys() {
 		BusinessGroup group1 = businessGroupDao.createAndPersist(null, "shorty-1", "shorty-1-desc", 0, 10, true, true, false, false, false);
 		BusinessGroup group2 = businessGroupDao.createAndPersist(null, "shorty-2", "shorty-2-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//check if the method is robust against empty list fo keys
 		List<BusinessGroupShort> groups1 = businessGroupDao.loadShort(Collections.<Long>emptyList());
 		Assert.assertNotNull(groups1);
 		Assert.assertEquals(0, groups1.size());
-		
+
 		//check load 1 group
 		List<BusinessGroupShort> groups2 = businessGroupDao.loadShort(Collections.singletonList(group1.getKey()));
 		Assert.assertNotNull(groups2);
 		Assert.assertEquals(1, groups2.size());
 		Assert.assertEquals(group1.getKey(), groups2.get(0).getKey());
 		Assert.assertEquals(group1.getName(), groups2.get(0).getName());
-		
+
 		//check load 2 groups
 		List<Long> groupKeys = new ArrayList<Long>(2);
 		groupKeys.add(group1.getKey());
@@ -273,14 +279,14 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(loadDescription);
 		Assert.assertEquals(description, loadDescription);
 	}
-	
+
 	@Test
 	public void loadAllBusinessGroups() {
 		Identity owner = JunitTestHelper.createAndPersistIdentityAsUser("bdao-3-" + UUID.randomUUID().toString());
 		BusinessGroup group1 = businessGroupDao.createAndPersist(owner, "gdgo", "gdgo-desc", 0, 10, true, true, false, false, false);
 		BusinessGroup group2 = businessGroupDao.createAndPersist(owner, "gdho", "gdho-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//load all business groups
 		List<BusinessGroup> allGroups = businessGroupDao.loadAll();
 		Assert.assertNotNull(allGroups);
@@ -288,14 +294,14 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(allGroups.contains(group1));
 		Assert.assertTrue(allGroups.contains(group2));
 	}
-	
+
 	@Test
 	public void mergeBusinessGroup() {
 		//create a business group
 		Identity owner = JunitTestHelper.createAndPersistIdentityAsUser("bdao-3-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupDao.createAndPersist(owner, "gdho", "gdho-desc", 0, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//delete a business group
 		group.setAutoCloseRanksEnabled(false);
 		group.setName("gdho-2");
@@ -306,9 +312,9 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals(group, mergedGroup);
 		Assert.assertEquals("gdho-2", mergedGroup.getName());
 		Assert.assertEquals(Boolean.FALSE, mergedGroup.getAutoCloseRanksEnabled());
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//reload the merged group and check values
 		BusinessGroup reloadedGroup = businessGroupDao.load(group.getKey());
 		Assert.assertNotNull(reloadedGroup);
@@ -316,14 +322,14 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals("gdho-2", reloadedGroup.getName());
 		Assert.assertEquals(Boolean.FALSE, reloadedGroup.getAutoCloseRanksEnabled());
 	}
-	
+
 	@Test
 	public void updateBusinessGroup() {
 		//create a business group
 		Identity owner = JunitTestHelper.createAndPersistIdentityAsUser("bdao-4-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupDao.createAndPersist(owner, "gdio", "gdio-desc", 1, 10, true, true, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//delete a business group
 		group.setWaitingListEnabled(false);
 		group.setDescription("gdio-2-desc");
@@ -335,9 +341,9 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals("gdio-2-desc", updatedGroup.getDescription());
 		Assert.assertEquals(Boolean.FALSE, updatedGroup.getWaitingListEnabled());
 		Assert.assertTrue(updatedGroup.equals(group));
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//reload the merged group and check values
 		BusinessGroup reloadedGroup = businessGroupDao.load(group.getKey());
 		Assert.assertNotNull(reloadedGroup);
@@ -345,7 +351,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals("gdio-2-desc", reloadedGroup.getDescription());
 		Assert.assertEquals(Boolean.FALSE, reloadedGroup.getWaitingListEnabled());
 	}
-	
+
 	@Test
 	public void findBusinessGroupsWithWaitingListAttendedBy() {
 		//3 identities
@@ -358,7 +364,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		BusinessGroup group2 = businessGroupDao.createAndPersist(null, "gdmo", "gdmo-desc", 0, 5, true, false, false, false, false);
 		BusinessGroup group3 = businessGroupDao.createAndPersist(null, "gdno", "gdno-desc", 0, 5, true, false, false, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//id1 -> group 1 and 2
 		businessGroupRelationDao.addRole(id1, group1, GroupRoles.waiting.name());
 		businessGroupRelationDao.addRole(id1, group2, GroupRoles.waiting.name());
@@ -382,7 +388,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(groupOfId3);
 		Assert.assertTrue(groupOfId3.isEmpty());
 	}
-	
+
 	@Test
 	public void testVisibilityOfSecurityGroups() {
 		//create 3 groups
@@ -390,21 +396,21 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		BusinessGroup group2 = businessGroupDao.createAndPersist(null, "gdso", "gdso-desc", 0, 5, true, false, false, true, false);
 		BusinessGroup group3 = businessGroupDao.createAndPersist(null, "gdto", "gdto-desc", 0, 5, true, false, false, false, true);
 		dbInstance.commitAndCloseSession();
-		
+
 		//check the value
 		Assert.assertTrue(group1.isOwnersVisibleIntern());
 		Assert.assertTrue(group1.isParticipantsVisibleIntern());
 		Assert.assertFalse(group1.isWaitingListVisibleIntern());
-		
+
 		Assert.assertFalse(group2.isOwnersVisibleIntern());
 		Assert.assertTrue(group2.isParticipantsVisibleIntern());
 		Assert.assertFalse(group2.isWaitingListVisibleIntern());
-		
+
 		Assert.assertFalse(group3.isOwnersVisibleIntern());
 		Assert.assertFalse(group3.isParticipantsVisibleIntern());
 		Assert.assertTrue(group3.isWaitingListVisibleIntern());
 	}
-	
+
 	@Test
 	public void findBusinessGroups() {
 		Identity identity = JunitTestHelper.createAndPersistIdentityAsRndUser("bg-search");
@@ -412,7 +418,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		BusinessGroup group2 = businessGroupDao.createAndPersist(null, "gdvo", "gdvo-desc", 0, 5, true, false, true, false, false);
 		dbInstance.commitAndCloseSession();
 
-		SearchBusinessGroupParams params = new SearchBusinessGroupParams(); 
+		SearchBusinessGroupParams params = new SearchBusinessGroupParams();
 		List<BusinessGroup> groups = businessGroupDao.findBusinessGroups(params, null, 0, -1);
 		Assert.assertNotNull(groups);
 		Assert.assertTrue(groups.size() >= 2);
@@ -429,11 +435,11 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		List<StatisticsBusinessGroupRow> groupToSelect = businessGroupDao.searchBusinessGroupsForSelection(searchParams, identity);
 		Assert.assertNotNull(groupToSelect);
 		Assert.assertTrue(groupToSelect.size() >= 2);
-		
+
 		List<OpenBusinessGroupRow> openGroups = businessGroupDao.searchPublishedBusinessGroups(searchParams, identity);
 		Assert.assertNotNull(openGroups);
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByExactName() {
 		String exactName = UUID.randomUUID().toString();
@@ -451,7 +457,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertFalse(groups.contains(group2));
 		Assert.assertFalse(groups.contains(group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByName() {
 		String marker = UUID.randomUUID().toString();
@@ -469,7 +475,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(groups.contains(group1));
 		Assert.assertTrue(groups.contains(group2));
 		Assert.assertFalse(groups.contains(group3));
-		
+
 		//check the same with the views
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
 		searchParams.setName(marker);
@@ -480,7 +486,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(contains(groupViews, group2));
 		Assert.assertFalse(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByNameFuzzy() {
 		String marker = UUID.randomUUID().toString();
@@ -498,7 +504,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(groups.contains(group1));
 		Assert.assertTrue(groups.contains(group2));
 		Assert.assertTrue(groups.contains(group3));
-		
+
 		//check the same with the views
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
 		searchParams.setName("*" + marker + "*");
@@ -509,7 +515,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(contains(groupViews, group2));
 		Assert.assertTrue(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByDescription() {
 		String marker = UUID.randomUUID().toString();
@@ -528,7 +534,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(groups.contains(group1));
 		Assert.assertFalse(groups.contains(group2));
 		Assert.assertFalse(groups.contains(group3));
-		
+
 		//check find business group
 
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
@@ -540,7 +546,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertFalse(contains(groupViews, group2));
 		Assert.assertFalse(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByDescriptionFuzzy() {
 		String marker = UUID.randomUUID().toString();
@@ -558,7 +564,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(groups.contains(group1));
 		Assert.assertTrue(groups.contains(group2));
 		Assert.assertTrue(groups.contains(group3));
-		
+
 		//check same search with the views
 
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
@@ -570,7 +576,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(contains(groupViews, group2));
 		Assert.assertTrue(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByNameOrDesc() {
 		String marker = UUID.randomUUID().toString();
@@ -588,7 +594,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(groups.contains(group1));
 		Assert.assertFalse(groups.contains(group2));
 		Assert.assertTrue(groups.contains(group3));
-		
+
 		//check the same search with the views
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
 		searchParams.setNameOrDesc(marker);
@@ -599,7 +605,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertFalse(contains(groupViews, group2));
 		Assert.assertTrue(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByNameOrDescFuzzy() {
 		String marker = UUID.randomUUID().toString();
@@ -617,7 +623,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(groups.contains(group1));
 		Assert.assertTrue(groups.contains(group2));
 		Assert.assertTrue(groups.contains(group3));
-		
+
 		//check the same search with the views
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
 		searchParams.setNameOrDesc("*" + marker + "*");
@@ -628,7 +634,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(contains(groupViews, group2));
 		Assert.assertTrue(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByOwner() {
 		//5 identities
@@ -641,7 +647,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		BusinessGroup group2 = businessGroupDao.createAndPersist(id2, "fingbgown-2", "fingbgown-2-desc", 0, 5, true, false, true, false, false);
 		BusinessGroup group3 = businessGroupDao.createAndPersist(id3, "fingbgown-3", "fingbgown-3-desc", 0, 5, true, false, true, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//check the same with the views
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
 		searchParams.setOwnerName(marker);
@@ -652,19 +658,19 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertFalse(contains(groupViews, group2));
 		Assert.assertTrue(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByOwnerFuzzy() {
 		String marker = UUID.randomUUID().toString();
 		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser(marker);
 		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("ddao-2-" + marker.toUpperCase());
 		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser(marker + "-ddao-3-");
-		
+
 		BusinessGroup group1 = businessGroupDao.createAndPersist(id1, "fingbg-own-1-1", "fingbg-own-1-1-desc", 0, 5, true, false, true, false, false);
 		BusinessGroup group2 = businessGroupDao.createAndPersist(id2, "fingbg-own-1-2", "fingbg-own-1-2-desc", 0, 5, true, false, true, false, false);
 		BusinessGroup group3 = businessGroupDao.createAndPersist(id3, "fingbg-own-1-3", "fingbg-own-1-3-desc", 0, 5, true, false, true, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//check the same with the views
 		BusinessGroupQueryParams searchParams = new BusinessGroupQueryParams();
 		searchParams.setOwnerName("*" + marker + "*");
@@ -675,27 +681,27 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(contains(groupViews, group2));
 		Assert.assertTrue(contains(groupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupWithAuthorConnection() {
 		Identity author = JunitTestHelper.createAndPersistIdentityAsUser("bdao-5-" + UUID.randomUUID().toString());
 		RepositoryEntry re = JunitTestHelper.createAndPersistRepositoryEntry();
 		repositoryEntryRelationDao.addRole(author, re, GroupRoles.owner.name());
-		
+
 		BusinessGroup group1 = businessGroupDao.createAndPersist(null, "gdlo", "gdlo-desc", 0, 5, true, false, false, false, false);
 		BusinessGroup group2 = businessGroupDao.createAndPersist(author, "gdmo", "gdmo-desc", 0, 5, true, false, false, false, false);
 		BusinessGroup group3 = businessGroupDao.createAndPersist(author, "gdmo", "gdmo-desc", 0, 5, true, false, false, false, false);
 		businessGroupRelationDao.addRelationToResource(group1, re);
 		businessGroupRelationDao.addRelationToResource(group3, re);
 		dbInstance.commitAndCloseSession();
-		
-		//check 
+
+		//check
 		BusinessGroupQueryParams params = new BusinessGroupQueryParams();
 		params.setAuthorConnection(true);
 		List<StatisticsBusinessGroupRow> groups = businessGroupDao.searchBusinessGroupsForSelection(params, author);
 		Assert.assertNotNull(groups);
 		Assert.assertEquals(2, groups.size());
-		
+
 		Set<Long> retrievedGroupkey = new HashSet<Long>();
 		for(StatisticsBusinessGroupRow group:groups) {
 			retrievedGroupkey.add(group.getKey());
@@ -704,7 +710,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(retrievedGroupkey.contains(group3.getKey()));
 		Assert.assertFalse(retrievedGroupkey.contains(group2.getKey()));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByIdentity() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-" + UUID.randomUUID().toString());
@@ -725,7 +731,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(ownedGroups);
 		Assert.assertEquals(1, ownedGroups.size());
 		Assert.assertTrue(ownedGroups.contains(group1));
-		
+
 		//check attendee
 		SearchBusinessGroupParams paramsAttendee = new SearchBusinessGroupParams();
 		paramsAttendee.setIdentity(id);
@@ -743,7 +749,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(waitingGroups);
 		Assert.assertEquals(1, waitingGroups.size());
 		Assert.assertTrue(waitingGroups.contains(group3));
-		
+
 		//check all
 		SearchBusinessGroupParams paramsAll = new SearchBusinessGroupParams();
 		paramsAll.setIdentity(id);
@@ -756,7 +762,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(allGroups.contains(group1));
 		Assert.assertTrue(allGroups.contains(group2));
 		Assert.assertTrue(allGroups.contains(group3));
-		
+
 		//The same tests with the views
 		//check owner on views
 		BusinessGroupQueryParams queryParamsOwner = new BusinessGroupQueryParams();
@@ -765,7 +771,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(ownedGroupViews);
 		Assert.assertEquals(1, ownedGroupViews.size());
 		Assert.assertTrue(contains(ownedGroupViews, group1));
-		
+
 		//check attendee on views
 		BusinessGroupQueryParams queryParamsAttendee = new BusinessGroupQueryParams();
 		queryParamsAttendee.setAttendee(true);
@@ -781,7 +787,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(waitingGroupViews);
 		Assert.assertEquals(1, waitingGroupViews.size());
 		Assert.assertTrue(contains(waitingGroupViews, group3));
-		
+
 		//check all on views
 		BusinessGroupQueryParams queryParamsAll = new BusinessGroupQueryParams();
 		queryParamsAll.setOwner(true);
@@ -794,7 +800,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(contains(allGroupViews, group2));
 		Assert.assertTrue(contains(allGroupViews, group3));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsByRepositoryEntry() {
 		//create a repository entry with a relation to a group
@@ -816,7 +822,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals(1, groupViews.size());
 		Assert.assertEquals(group.getKey(), groupViews.get(0).getKey());
 	}
-	
+
 
 	@Test
 	public void findBusinessGroupsByCourseTitle() {
@@ -839,7 +845,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals(1, groupViews.size());
 		Assert.assertEquals(group.getKey(), groupViews.get(0).getKey());
 	}
-	
+
 	@Test
 	public void findManagedGroups() {
 		//create a managed group with an external ID
@@ -859,7 +865,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(managedGroups.size() >= 1);
 		Assert.assertTrue(managedGroups.contains(managedGroup));
 		Assert.assertFalse(managedGroups.contains(freeGroup));
-		
+
 		//search free group
 		SearchBusinessGroupParams paramsAll = new SearchBusinessGroupParams();
 		paramsAll.setManaged(Boolean.FALSE);
@@ -869,8 +875,8 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(freeGroups.contains(freeGroup));
 		Assert.assertFalse(freeGroups.contains(managedGroup));
 	}
-	
-	
+
+
 	@Test
 	public void findGroupByExternalId() {
 		//create a managed group with an external ID
@@ -879,7 +885,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		BusinessGroup group = businessGroupDao.createAndPersist(null, "managed-grp-2", "managed-grp-2-desc",
 				externalId, managedFlags, 0, 5, true, false, true, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//search
 		SearchBusinessGroupParams paramsAll = new SearchBusinessGroupParams();
 		paramsAll.setExternalId(externalId);
@@ -898,9 +904,15 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Offer offer = acService.createOffer(group.getResource(), "TestBGWorkflow");
 		assertNotNull(offer);
 		offer = acService.save(offer);
-			
+
+		acMethodManager.enableMethod(TokenAccessMethod.class, true);
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
+		AccessMethod method = methods.get(0);
+		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
+		acMethodManager.save(access);
+
 		dbInstance.commitAndCloseSession();
-			
+
 		//retrieve the offer
 		//check the search with the views
 		BusinessGroupQueryParams queryAllParams = new BusinessGroupQueryParams();
@@ -909,7 +921,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertNotNull(accessGroupViews);
 		Assert.assertTrue(accessGroupViews.size() >= 1);
 		Assert.assertTrue(contains(accessGroupViews, group));
-		
+
 		for(OpenBusinessGroupRow accessGroup:accessGroupViews) {
 			OLATResource resource = resourceManager.findResourceById(accessGroup.getResourceKey());
 			List<Offer> offers = acService.findOfferByResource(resource, true, new Date());
@@ -917,7 +929,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 			Assert.assertFalse(offers.isEmpty());
 		}
 	}
-	
+
 	@Test
 	public void findPublicGroupsLimitedDate() {
 		//create a group with an access control limited by a valid date
@@ -934,6 +946,12 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		assertNotNull(offer);
 		offer = acService.save(offer);
 
+		acMethodManager.enableMethod(TokenAccessMethod.class, true);
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
+		AccessMethod method = methods.get(0);
+		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
+		acMethodManager.save(access);
+
 		//create a group with an access control limited by dates in the past
 		BusinessGroup oldGroup = businessGroupDao.createAndPersist(null, "access-grp-3", "access-grp-3-desc", 0, 5, true, false, true, false, false);
 		//create and save an offer
@@ -945,8 +963,11 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		assertNotNull(oldOffer);
 		oldOffer = acService.save(oldOffer);
 
+		OfferAccess oldAccess = acMethodManager.createOfferAccess(oldOffer, method);
+		acMethodManager.save(oldAccess);
+
 		dbInstance.commitAndCloseSession();
-			
+
 		//retrieve the offer
 		BusinessGroupQueryParams paramsAll = new BusinessGroupQueryParams();
 		paramsAll.setPublicGroups(Boolean.TRUE);
@@ -955,7 +976,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(accessGroups.size() >= 1);
 		Assert.assertTrue(contains(accessGroups, groupVisible));
 		Assert.assertFalse(contains(accessGroups, oldGroup));
-	}	
+	}
 
 	@Test
 	public void findBusinessGroupsWithResources() {
@@ -985,7 +1006,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertFalse(groupViewWithout.isEmpty());
 		Assert.assertTrue(contains(groupViewWithout, group2));
 	}
-	
+
 	@Test
 	public void findMarkedBusinessGroup() {
 		Identity marker = JunitTestHelper.createAndPersistIdentityAsUser("marker-" + UUID.randomUUID().toString());
@@ -994,7 +1015,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		BusinessGroup group2 = businessGroupDao.createAndPersist(marker, "marked-grp-2", "marked-grp-2-desc", 0, 5, true, false, true, false, false);
 		markManager.setMark(group1.getResource(), marker, null, "[BusinessGroup:" + group1.getKey() + "]");
 		dbInstance.commitAndCloseSession();
-		
+
 		//check the search with the views
 		//check marked
 		BusinessGroupQueryParams queryMarkedParams = new BusinessGroupQueryParams();
@@ -1017,7 +1038,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		markManager.setMark(group1.getResource(), marker1, null, "[BusinessGroup:" + group1.getKey() + "]");
 		markManager.setMark(group2.getResource(), marker2, null, "[BusinessGroup:" + group2.getKey() + "]");
 		dbInstance.commitAndCloseSession();
-		
+
 		//check the search with views
 		//check marked
 		BusinessGroupQueryParams queryParamsMarker1 = new BusinessGroupQueryParams();
@@ -1027,14 +1048,14 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals(1, markedGroupViews.size());
 		Assert.assertTrue(contains(markedGroupViews, group1));
 	}
-	
+
 	@Test
 	public void findBusinessGroupsHeadless() {
 		Identity owner = JunitTestHelper.createAndPersistIdentityAsUser("head-1-" + UUID.randomUUID().toString());
 		BusinessGroup headlessGroup = businessGroupDao.createAndPersist(null, "headless-grp", "headless-grp-desc", 0, 5, true, false, true, false, false);
 		BusinessGroup headedGroup = businessGroupDao.createAndPersist(owner, "headed-grp", "headed-grp-desc", 0, 5, true, false, true, false, false);
 		dbInstance.commitAndCloseSession();
-		
+
 		//check marked
 		BusinessGroupQueryParams headlessParams = new BusinessGroupQueryParams();
 		headlessParams.setHeadless(true);
@@ -1044,7 +1065,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(contains(groups, headlessGroup));
 		Assert.assertFalse(contains(groups, headedGroup));
 	}
-	
+
 	@Test
 	public void findBusinessGroups_my() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-" + UUID.randomUUID().toString());
@@ -1065,7 +1086,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertTrue(originalKeys.contains(group2.getKey()));
 		Assert.assertFalse(originalKeys.contains(group3.getKey()));
 	}
-	
+
 	@Test
 	public void isIdentityInBusinessGroups() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-" + UUID.randomUUID().toString());
@@ -1077,7 +1098,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		businessGroupRelationDao.addRole(id, group2, GroupRoles.participant.name());
 		businessGroupRelationDao.addRole(id, group3, GroupRoles.waiting.name());
 		dbInstance.commitAndCloseSession();
-		
+
 		List<BusinessGroup> groups = new ArrayList<BusinessGroup>();
 		groups.add(group1);
 		groups.add(group2);
@@ -1089,20 +1110,20 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals(2, groupKeysA.size());
 		Assert.assertTrue(groupKeysA.contains(group1.getKey()));
 		Assert.assertTrue(groupKeysA.contains(group2.getKey()));
-		
-		//check owner 
+
+		//check owner
 		List<Long> groupKeysB = businessGroupDao.isIdentityInBusinessGroups(id, true, false, false, groups);
 		Assert.assertNotNull(groupKeysB);
 		Assert.assertEquals(1, groupKeysB.size());
 		Assert.assertTrue(groupKeysB.contains(group1.getKey()));
 
-		//check attendee 
+		//check attendee
 		List<Long> groupKeysC = businessGroupDao.isIdentityInBusinessGroups(id, false, true, false, groups);
 		Assert.assertNotNull(groupKeysC);
 		Assert.assertEquals(1, groupKeysC.size());
 		Assert.assertTrue(groupKeysC.contains(group2.getKey()));
 	}
-	
+
 	@Test
 	public void getMembershipInfoInBusinessGroups() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-" + UUID.randomUUID().toString());
@@ -1114,7 +1135,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		businessGroupRelationDao.addRole(id, group2, GroupRoles.participant.name());
 		businessGroupRelationDao.addRole(id, group3, GroupRoles.waiting.name());
 		dbInstance.commitAndCloseSession();
-		
+
 		List<Long> groupKeys = new ArrayList<Long>();
 		groupKeys.add(group1.getKey());
 		groupKeys.add(group2.getKey());
@@ -1126,7 +1147,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		List<BusinessGroupMembershipViewImpl> memberships = businessGroupDao.getMembershipInfoInBusinessGroups(groupKeys, id);
 		Assert.assertNotNull(memberships);
 		Assert.assertEquals(3, memberships.size());
-		
+
 		int found = 0;
 		for(BusinessGroupMembershipViewImpl membership:memberships) {
 			Assert.assertNotNull(membership.getIdentityKey());
@@ -1144,7 +1165,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		}
 		Assert.assertEquals(3, found);
 	}
-	
+
 	@Test
 	public void getBusinessGroupsMembership() {
 		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-rev-1" + UUID.randomUUID().toString());
@@ -1159,7 +1180,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		businessGroupRelationDao.addRole(id2, group2, GroupRoles.participant.name());
 		businessGroupRelationDao.addRole(id2, group3, GroupRoles.participant.name());
 		dbInstance.commitAndCloseSession();
-		
+
 		List<BusinessGroup> groups = new ArrayList<BusinessGroup>();
 		groups.add(group1);
 		groups.add(group2);
@@ -1202,14 +1223,14 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		Assert.assertEquals("Participants", 3, foundPart);
 		Assert.assertEquals("Waiting", 1, foundWait);
 	}
-	
-	
+
+
 	@Test
 	public void getMembershipInfoInBusinessGroupsWithoutIdentityParam() {
 		Identity id1 = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-" + UUID.randomUUID().toString());
 		Identity id2 = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-" + UUID.randomUUID().toString());
 		Identity id3 = JunitTestHelper.createAndPersistIdentityAsUser("is-in-grp-" + UUID.randomUUID().toString());
-		
+
 		BusinessGroup group1 = businessGroupDao.createAndPersist(id1, "is-in-grp-1", "is-in-grp-1-desc", 0, 5, true, false, true, false, false);
 		BusinessGroup group2 = businessGroupDao.createAndPersist(id2, "is-in-grp-2", "is-in-grp-2-desc", 0, 5, true, false, true, false, false);
 		BusinessGroup group3 = businessGroupDao.createAndPersist(null, "is-in-grp-3", "is-in-grp-3-desc", 0, 5, true, false, true, false, false);
@@ -1221,7 +1242,7 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 		businessGroupRelationDao.addRole(id3, group2, GroupRoles.waiting.name());
 		businessGroupRelationDao.addRole(id3, group3, GroupRoles.participant.name());
 		dbInstance.commitAndCloseSession();
-		
+
 		List<Long> groupKeys = new ArrayList<Long>();
 		groupKeys.add(group1.getKey());
 		groupKeys.add(group2.getKey());
@@ -1237,12 +1258,12 @@ public class BusinessGroupDAOTest extends OlatTestCase {
 			Assert.assertNotNull(membership.getLastModified());
 		}
 	}
-	
+
 	private boolean contains(List<? extends BusinessGroupRef> rows, BusinessGroup group) {
 		if(rows != null && !rows.isEmpty()) {
 			for(BusinessGroupRef row:rows) {
 				if(row.getKey().equals(group.getKey())) {
-					return true; 
+					return true;
 				}
 			}
 		}
diff --git a/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java b/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java
index 0bda248053fd043fa333107f4cd523b9fd6d9cf9..cd76c8d9c789d739b2d29755c168242333e01447 100644
--- a/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java
+++ b/src/test/java/org/olat/repository/manager/RepositoryEntryDAOTest.java
@@ -20,6 +20,7 @@
 package org.olat.repository.manager;
 
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
 import java.util.UUID;
@@ -35,13 +36,13 @@ import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
- * 
+ *
  * Initial date: 12.03.2014<br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  *
  */
 public class RepositoryEntryDAOTest extends OlatTestCase {
-	
+
 	@Autowired
 	private DB dbInstance;
 	@Autowired
@@ -50,52 +51,52 @@ public class RepositoryEntryDAOTest extends OlatTestCase {
 	private RepositoryService repositoryService;
 	@Autowired
 	private RepositoryEntryDAO repositoryEntryDao;
-	
+
 	@Test
 	public void loadByKey() {
 		RepositoryEntry re = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 1", "", null);
 		dbInstance.commitAndCloseSession();
 		Assert.assertNotNull(re);
-		
+
 		RepositoryEntry loadedRe = repositoryEntryDao.loadByKey(re.getKey());
 		Assert.assertNotNull(loadedRe.getStatistics());
 		Assert.assertNotNull(loadedRe.getOlatResource());
 	}
-	
+
 	@Test
 	public void loadByResourceKey() {
 		RepositoryEntry re = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 2", "", null);
 		dbInstance.commitAndCloseSession();
 		Assert.assertNotNull(re);
-		
+
 		RepositoryEntry loadedRe = repositoryEntryDao.loadByResourceKey(re.getOlatResource().getKey());
 		Assert.assertNotNull(loadedRe.getStatistics());
 		Assert.assertEquals(re.getOlatResource(), loadedRe.getOlatResource());
 	}
-	
+
 	@Test
 	public void loadByResourceKeys() {
 		RepositoryEntry re1 = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 3a", "", null);
 		RepositoryEntry re2 = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 3b", "", null);
 		dbInstance.commitAndCloseSession();
-		
+
 		List<Long> resourceKeys = new ArrayList<>(2);
 		resourceKeys.add(re1.getOlatResource().getKey());
 		resourceKeys.add(re2.getOlatResource().getKey());
-		
+
 		//load 2 resources
 		List<RepositoryEntry> loadedRes = repositoryEntryDao.loadByResourceKeys(resourceKeys);
 		Assert.assertNotNull(loadedRes);
 		Assert.assertEquals(2,  loadedRes.size());
 		Assert.assertTrue(loadedRes.contains(re1));
 		Assert.assertTrue(loadedRes.contains(re2));
-		
+
 		//try with empty list
 		List<RepositoryEntry> emptyRes = repositoryEntryDao.loadByResourceKeys(Collections.<Long>emptyList());
 		Assert.assertNotNull(emptyRes);
 		Assert.assertEquals(0,  emptyRes.size());
 	}
-	
+
 	@Test
 	public void searchByIdAndRefs() {
 		RepositoryEntry re = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 4", "", null);
@@ -104,58 +105,58 @@ public class RepositoryEntryDAOTest extends OlatTestCase {
 		String externalRef = UUID.randomUUID().toString();
 		re = repositoryManager.setDescriptionAndName(re, null, null, null, null, externalId, externalRef, null, null);
 		dbInstance.commitAndCloseSession();
-		
+
 		//by primary key
 		List<RepositoryEntry> primaryKeyList = repositoryEntryDao.searchByIdAndRefs(Long.toString(re.getKey()));
 		Assert.assertNotNull(primaryKeyList);
 		Assert.assertEquals(1,  primaryKeyList.size());
 		Assert.assertEquals(re, primaryKeyList.get(0));
-		
+
 		//by soft key
 		List<RepositoryEntry> softKeyList = repositoryEntryDao.searchByIdAndRefs(re.getSoftkey());
 		Assert.assertNotNull(softKeyList);
 		Assert.assertEquals(1, softKeyList.size());
 		Assert.assertEquals(re, softKeyList.get(0));
-		
+
 		//by resourceable id key
 		List<RepositoryEntry> resourceableIdList = repositoryEntryDao.searchByIdAndRefs(Long.toString(re.getResourceableId()));
 		Assert.assertNotNull(resourceableIdList);
 		Assert.assertEquals(1, resourceableIdList.size());
 		Assert.assertEquals(re, resourceableIdList.get(0));
-		
+
 		//by resource resourceable id
 		Long resResourceableId = re.getOlatResource().getResourceableId();
 		List<RepositoryEntry> resResourceableIdList = repositoryEntryDao.searchByIdAndRefs(resResourceableId.toString());
 		Assert.assertNotNull(resResourceableIdList);
 		Assert.assertEquals(1,  resResourceableIdList.size());
 		Assert.assertEquals(re, resResourceableIdList.get(0));
-		
+
 		//by external id
 		List<RepositoryEntry> externalIdList = repositoryEntryDao.searchByIdAndRefs(externalId);
 		Assert.assertNotNull(externalIdList);
 		Assert.assertEquals(1,  externalIdList.size());
 		Assert.assertEquals(re, externalIdList.get(0));
-		
+
 		//by external ref
 		List<RepositoryEntry> externalRefList = repositoryEntryDao.searchByIdAndRefs(externalRef);
 		Assert.assertNotNull(externalRefList);
 		Assert.assertEquals(1, externalRefList.size());
 		Assert.assertEquals(re, externalRefList.get(0));
-		
+
 	}
-	
+
 	@Test
 	public void getAllRepositoryEntries() {
 		RepositoryEntry re = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 4", "", null);
 		dbInstance.commitAndCloseSession();
 		Assert.assertNotNull(re);
-		
+
 		List<RepositoryEntry> allRes = repositoryEntryDao.getAllRepositoryEntries(0, 25);
 		Assert.assertNotNull(allRes);
 		Assert.assertFalse(allRes.isEmpty());
 		Assert.assertTrue(allRes.size() < 26);
 	}
-	
+
 	@Test
 	public void loadRepositoryEntryResource() {
 		RepositoryEntry re = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 5", "", null);
@@ -163,12 +164,12 @@ public class RepositoryEntryDAOTest extends OlatTestCase {
 		Assert.assertNotNull(re);
 		Assert.assertNotNull(re.getSoftkey());
 		Assert.assertNotNull(re.getOlatResource());
-		
+
 		OLATResource loadedResource = repositoryEntryDao.loadRepositoryEntryResource(re.getKey());
 		Assert.assertNotNull(loadedResource);
 		Assert.assertEquals(re.getOlatResource(), loadedResource);
 	}
-	
+
 	@Test
 	public void loadRepositoryEntryResourceBySoftKey() {
 		RepositoryEntry re = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 5", "", null);
@@ -176,9 +177,69 @@ public class RepositoryEntryDAOTest extends OlatTestCase {
 		Assert.assertNotNull(re);
 		Assert.assertNotNull(re.getSoftkey());
 		Assert.assertNotNull(re.getOlatResource());
-		
+
 		OLATResource loadedResource = repositoryEntryDao.loadRepositoryEntryResourceBySoftKey(re.getSoftkey());
 		Assert.assertNotNull(loadedResource);
 		Assert.assertEquals(re.getOlatResource(), loadedResource);
 	}
+
+	@Test
+	public void loadRepositoryEntriesByExternalId() {
+		String externalId = "myExternalId";
+
+		// remove old test date
+		String query = "update repositoryentry as v set v.externalId=null where v.externalId=:externalId";
+		dbInstance.getCurrentEntityManager()
+				.createQuery(query)
+				.setParameter("externalId", externalId)
+				.executeUpdate();
+
+		// insert test data
+		RepositoryEntry re1 = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 7a", "", null);
+		re1.setExternalId(externalId);
+		repositoryService.update(re1);
+		RepositoryEntry re2 = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 7b", "", null);
+		re2.setExternalId(externalId);
+		repositoryService.update(re2);
+		dbInstance.commitAndCloseSession();
+
+		Collection<RepositoryEntry> entries = repositoryEntryDao.loadRepositoryEntriesByExternalId(externalId);
+		Assert.assertNotNull(entries);
+		Assert.assertEquals(2, entries.size());
+
+		//try with null
+		Collection<RepositoryEntry> emptyRes = repositoryEntryDao.loadRepositoryEntriesByExternalId(null);
+		Assert.assertNotNull(emptyRes);
+		Assert.assertEquals(0, emptyRes.size());
+	}
+
+	@Test
+	public void loadRepositoryEntriesByExternalRef() {
+		String externalRef = "myExternalRef";
+
+		// remove old test date
+		String query = "update repositoryentry as v set v.externalRef=null where v.externalRef=:externalRef";
+		dbInstance.getCurrentEntityManager()
+				.createQuery(query)
+				.setParameter("externalRef", externalRef)
+				.executeUpdate();
+
+		// insert test data
+		RepositoryEntry re1 = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 8a", "", null);
+		re1.setExternalRef(externalRef);
+		repositoryService.update(re1);
+		RepositoryEntry re2 = repositoryService.create("Rei Ayanami", "-", "Repository entry DAO Test 8b", "", null);
+		re2.setExternalRef(externalRef);
+		repositoryService.update(re2);
+		dbInstance.commitAndCloseSession();
+
+		Collection<RepositoryEntry> entries = repositoryEntryDao.loadRepositoryEntriesByExternalRef(externalRef);
+		Assert.assertNotNull(entries);
+		Assert.assertEquals(2, entries.size());
+
+		//try with null
+		Collection<RepositoryEntry> emptyRes = repositoryEntryDao.loadRepositoryEntriesByExternalRef(null);
+		Assert.assertNotNull(emptyRes);
+		Assert.assertEquals(0, emptyRes.size());
+	}
 }
\ No newline at end of file
diff --git a/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java b/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java
index 110d463d5948bbe05aa4303cee5dc8a3949d9f31..89bc082f964d5444bd5a380980357fd4b41f029b 100644
--- a/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java
+++ b/src/test/java/org/olat/resource/accesscontrol/ACFrontendManagerTest.java
@@ -47,22 +47,23 @@ import org.olat.resource.accesscontrol.manager.ACMethodDAO;
 import org.olat.resource.accesscontrol.manager.ACOfferDAO;
 import org.olat.resource.accesscontrol.model.AccessMethod;
 import org.olat.resource.accesscontrol.model.FreeAccessMethod;
+import org.olat.resource.accesscontrol.model.TokenAccessMethod;
 import org.olat.resource.accesscontrol.provider.paypal.model.PaypalAccessMethod;
 import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
- * 
+ *
  * Description:<br>
  * Test the frontend manager
- * 
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
  */
 public class ACFrontendManagerTest extends OlatTestCase {
-	
+
 	@Autowired
 	private DB dbInstance;
 	@Autowired
@@ -85,7 +86,7 @@ public class ACFrontendManagerTest extends OlatTestCase {
 	private ACMethodDAO acMethodManager;
 	@Autowired
 	private AccessControlModule acModule;
-	
+
 	@Test
 	public void testManagers() {
 		assertNotNull(acOfferManager);
@@ -95,19 +96,27 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		assertNotNull(repositoryManager);
 		assertNotNull(securityManager);
 	}
-	
+
 	@Test
 	public void testRepoWorkflow() {
 		//create a repository entry
 		RepositoryEntry re = createRepositoryEntry();
 		assertNotNull(re);
-		
+
 		//create and save an offer
 		Offer offer = acService.createOffer(re.getOlatResource(), "TestRepoWorkflow");
 		assertNotNull(offer);
 		offer = acService.save(offer);
 		dbInstance.commitAndCloseSession();
-		
+
+		//create a link offer to method
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
+		AccessMethod method = methods.get(0);
+		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
+		acMethodManager.save(access);
+
+		dbInstance.commitAndCloseSession();
+
 		//retrieve the offer
 		List<Offer> offers = acService.findOfferByResource(re.getOlatResource(), true, null);
 		assertEquals(1, offers.size());
@@ -116,7 +125,7 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		assertNotNull(savedOffer.getResource());
 		assertTrue(re.getOlatResource().equalsByPersistableKey(savedOffer.getResource()));
 	}
-	
+
 	/**
 	 * Test free access to a group without waiting list
 	 */
@@ -126,23 +135,23 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "Really free", null, null, false, false, null);
 		Offer offer = acService.createOffer(group.getResource(), "FreeGroup");
-		offer = acService.save(offer);	
+		offer = acService.save(offer);
 		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
 		Assert.assertNotNull(offerAccess);
 		dbInstance.commitAndCloseSession();
-		
+
 		//access it
 		AccessResult result = acService.accessResource(id, offerAccess, null);
 		Assert.assertNotNull(result);
 		Assert.assertTrue(result.isAccessible());
 		dbInstance.commitAndCloseSession();
-		
+
 		//is id a participant?
 		boolean participant = businessGroupRelationDao.hasRole(id, group, GroupRoles.participant.name());
 		Assert.assertTrue(participant);
 	}
-	
+
 	/**
 	 * Test free access to a group without waiting list and which is full
 	 */
@@ -155,27 +164,27 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), false, false, null);
 		businessGroupRelationDao.addRole(id1, group, GroupRoles.participant.name());
 		businessGroupRelationDao.addRole(id2, group, GroupRoles.participant.name());
-		
+
 		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
-		offer = acService.save(offer);	
+		offer = acService.save(offer);
 		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
 		Assert.assertNotNull(offerAccess);
 		dbInstance.commitAndCloseSession();
-		
+
 		//access it
 		AccessResult result = acService.accessResource(id3, offerAccess, null);
 		Assert.assertNotNull(result);
 		Assert.assertFalse(result.isAccessible());
 		dbInstance.commitAndCloseSession();
-		
+
 		//is id a waiting?
 		boolean participant = businessGroupRelationDao.hasRole(id3, group, GroupRoles.participant.name());
 		Assert.assertFalse(participant);
 		boolean waiting = businessGroupRelationDao.hasRole(id3, group, GroupRoles.waiting.name());
 		Assert.assertFalse(waiting);
 	}
-	
+
 	/**
 	 * Test free access to a group with waiting list enough place
 	 */
@@ -185,25 +194,25 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("agp-" + UUID.randomUUID().toString());
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(10), true, false, null);
 		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
-		offer = acService.save(offer);	
+		offer = acService.save(offer);
 		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
 		Assert.assertNotNull(offerAccess);
 		dbInstance.commitAndCloseSession();
-		
+
 		//access it
 		AccessResult result = acService.accessResource(id, offerAccess, null);
 		Assert.assertNotNull(result);
 		Assert.assertTrue(result.isAccessible());
 		dbInstance.commitAndCloseSession();
-		
+
 		//is id a waiting?
 		boolean participant = businessGroupRelationDao.hasRole(id, group, GroupRoles.participant.name());
 		Assert.assertTrue(participant);
 		boolean waiting = businessGroupRelationDao.hasRole(id, group, GroupRoles.waiting.name());
 		Assert.assertFalse(waiting);
 	}
-	
+
 	/**
 	 * Test free access to a group with waiting list enough place
 	 */
@@ -216,28 +225,28 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), true, false, null);
 		businessGroupRelationDao.addRole(id1, group, GroupRoles.participant.name());
 		businessGroupRelationDao.addRole(id2, group, GroupRoles.participant.name());
-		
+
 		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
-		offer = acService.save(offer);	
+		offer = acService.save(offer);
 		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		OfferAccess offerAccess = acService.createOfferAccess(offer, freeMethods.get(0));
 		Assert.assertNotNull(offerAccess);
 		dbInstance.commitAndCloseSession();
-		
+
 		//access it
 		AccessResult result = acService.accessResource(id3, offerAccess, null);
 		Assert.assertNotNull(result);
 		Assert.assertTrue(result.isAccessible());
 		dbInstance.commitAndCloseSession();
-		
+
 		//is id a waiting?
 		boolean participant = businessGroupRelationDao.hasRole(id3, group, GroupRoles.participant.name());
 		Assert.assertFalse(participant);
 		boolean waiting = businessGroupRelationDao.hasRole(id3, group, GroupRoles.waiting.name());
 		Assert.assertTrue(waiting);
 	}
-	
-	
+
+
 	/**
 	 * Test paypal scenario where a user begin the process to pay an access
 	 * to a group while an administrator is filling the group,
@@ -257,40 +266,40 @@ public class ACFrontendManagerTest extends OlatTestCase {
 
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), true, false, null);
 		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
-		offer = acService.save(offer);	
+		offer = acService.save(offer);
 		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(PaypalAccessMethod.class);
 		Assert.assertFalse(methods.isEmpty());
 		OfferAccess offerAccess = acService.createOfferAccess(offer, methods.get(0));
 		Assert.assertNotNull(offerAccess);
 		dbInstance.commitAndCloseSession();
-		
+
 		//id1 start payment process
 		boolean reserved = acService.reserveAccessToResource(id1, offerAccess);
 		Assert.assertTrue(reserved);
 		dbInstance.commitAndCloseSession();
-		
+
 		//admin fill the group
 		businessGroupRelationDao.addRole(id2, group, GroupRoles.participant.name());
 		businessGroupRelationDao.addRole(id3, group, GroupRoles.participant.name());
 		dbInstance.commitAndCloseSession();
-		
+
 		//id1 finish the process
 		AccessResult result = acService.accessResource(id1, offerAccess, null);
 		Assert.assertNotNull(result);
 		Assert.assertTrue(result.isAccessible());
 		dbInstance.commitAndCloseSession();
-		
+
 		//is id a waiting?
 		boolean participant = businessGroupRelationDao.hasRole(id1, group, GroupRoles.participant.name());
 		Assert.assertTrue(participant);
 		boolean waiting = businessGroupRelationDao.hasRole(id1, group, GroupRoles.waiting.name());
 		Assert.assertFalse(waiting);
-		
+
 		if(!enabled) {
 			acModule.setPaypalEnabled(false);
 		}
 	}
-	
+
 	@Test
 	public void testPaiedAccesToBusinessGroup_full() {
 		//enable paypal
@@ -306,7 +315,7 @@ public class ACFrontendManagerTest extends OlatTestCase {
 
 		BusinessGroup group = businessGroupService.createBusinessGroup(null, "Free group", "But you must wait", new Integer(0), new Integer(2), false, false, null);
 		Offer offer = acService.createOffer(group.getResource(), "Free group (waiting)");
-		offer = acService.save(offer);	
+		offer = acService.save(offer);
 		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(PaypalAccessMethod.class);
 		Assert.assertFalse(methods.isEmpty());
 		OfferAccess offerAccess = acService.createOfferAccess(offer, methods.get(0));
@@ -321,21 +330,21 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		//id1 try to reserve a place before the payment process
 		boolean reserved = acService.reserveAccessToResource(id1, offerAccess);
 		Assert.assertFalse(reserved);
-		
+
 		if(!enabled) {
 			acModule.setPaypalEnabled(false);
 		}
 	}
-	
+
 	@Test
 	public void makeAccessible() {
 		Identity id = JunitTestHelper.createAndPersistIdentityAsUser("acc-" + UUID.randomUUID());
 		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		AccessMethod method = methods.get(0);
-		
+
 		RepositoryEntry re = createRepositoryEntry();
 		Assert.assertNotNull(re);
-		
+
 		//create an offer to buy
 		OLATResource randomOres = re.getOlatResource();
 		Offer offer = acService.createOffer(randomOres, "Test auto access");
@@ -344,15 +353,15 @@ public class ACFrontendManagerTest extends OlatTestCase {
 		offer = acService.save(offer);
 		acService.saveOfferAccess(link);
 		dbInstance.commit();
-	
+
 		long start = System.nanoTime();
 		AccessResult acResult = acService.isAccessible(re, id, false, true);
 		Assert.assertNotNull(acResult);
-		Assert.assertTrue(acResult.isAccessible());	
+		Assert.assertTrue(acResult.isAccessible());
 		dbInstance.commit();
 		CodeHelper.printNanoTime(start, "One click");
 	}
-	
+
 	private RepositoryEntry createRepositoryEntry() {
 		//create a repository entry
 		OLATResourceable resourceable = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
diff --git a/src/test/java/org/olat/resource/accesscontrol/ACMethodManagerTest.java b/src/test/java/org/olat/resource/accesscontrol/ACMethodManagerTest.java
index 1baef22cfe3e263e960cfe13291e3e9aa238564f..ad635fa556fa1ee8f8e6542cacb160306f9223de 100644
--- a/src/test/java/org/olat/resource/accesscontrol/ACMethodManagerTest.java
+++ b/src/test/java/org/olat/resource/accesscontrol/ACMethodManagerTest.java
@@ -21,6 +21,7 @@
 package org.olat.resource.accesscontrol;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 
@@ -44,15 +45,16 @@ import org.olat.resource.accesscontrol.manager.ACOfferDAO;
 import org.olat.resource.accesscontrol.model.AccessMethod;
 import org.olat.resource.accesscontrol.model.FreeAccessMethod;
 import org.olat.resource.accesscontrol.model.TokenAccessMethod;
+import org.olat.shibboleth.manager.ShibbolethAutoAccessMethod;
 import org.olat.test.JunitTestHelper;
 import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
- * 
+ *
  * Description:<br>
  * Test the payment manager
- * 
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
@@ -61,29 +63,30 @@ public class ACMethodManagerTest extends OlatTestCase {
 
 	private static Identity ident1;
 	private static boolean isInitialized = false;
-	
+
 	@Autowired
 	private DB dbInstance;
-	
+
 	@Autowired
 	private ACOfferDAO acOfferManager;
-	
+
 	@Autowired
 	private ACService acService;
-	
+
 	@Autowired
 	private ACMethodDAO acMethodManager;
 
 	@Autowired
 	private OLATResourceManager resourceManager;
-	
+
 	@Before
 	public void setUp() {
 		if(!isInitialized) {
 			ident1 = JunitTestHelper.createAndPersistIdentityAsRndUser("ac-method-mgr");
 		}
+		acMethodManager.enableMethod(ShibbolethAutoAccessMethod.class, true);
 	}
-	
+
 	@Test
 	public void testManagers() {
 		assertNotNull(acOfferManager);
@@ -91,34 +94,41 @@ public class ACMethodManagerTest extends OlatTestCase {
 		assertNotNull(dbInstance);
 		assertNotNull(acMethodManager);
 	}
-	
+
 	@Test
 	public void testTokenMethod() {
 		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
 		assertNotNull(methods);
 		assertEquals(1, methods.size());
 	}
-	
+
 	@Test
 	public void testFreeMethod() {
 		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		assertNotNull(methods);
 		assertEquals(1, methods.size());
 	}
-	
+
+	@Test
+	public void testAutoShibMethod() {
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(ShibbolethAutoAccessMethod.class);
+		assertNotNull(methods);
+		assertEquals(1, methods.size());
+	}
+
 	@Test
 	public void testStandardMethods() {
 		Roles roles = new Roles(false, false, false, true, false, false, false);
 		List<AccessMethod> methods = acMethodManager.getAvailableMethods(ident1, roles);
 		assertNotNull(methods);
 		assertTrue(methods.size() >= 2);
-		
+
 		Set<String> duplicateTypes = new HashSet<>();
-		
+
 		boolean foundFree = false;
 		boolean foundToken = false;
 		for(AccessMethod method:methods) {
-			Assert.assertFalse(duplicateTypes.contains(method.getType()));	
+			Assert.assertFalse(duplicateTypes.contains(method.getType()));
 			if(method instanceof FreeAccessMethod) {
 				foundFree = true;
 			} else if(method instanceof TokenAccessMethod) {
@@ -131,7 +141,58 @@ public class ACMethodManagerTest extends OlatTestCase {
 		assertTrue(foundFree);
 		assertTrue(foundToken);
 	}
-	
+
+	@Test
+	public void testIsValidMethodAvailable() {
+		//create a resource and an offer
+		OLATResource randomOres = createResource();
+		Offer offer = acService.createOffer(randomOres, "TestIsValidMethodAvailable");
+		offer = acService.save(offer);
+		dbInstance.commitAndCloseSession();
+
+		//create a link offer to gui method
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
+		AccessMethod method = methods.get(0);
+		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
+		acMethodManager.save(access);
+
+		//create a link offer to gui method
+		List<AccessMethod> methodsNonGUI = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
+		AccessMethod methodNonGUI = methodsNonGUI.get(0);
+		OfferAccess accessNonGUI = acMethodManager.createOfferAccess(offer, methodNonGUI);
+		acMethodManager.save(accessNonGUI);
+
+		dbInstance.commitAndCloseSession();
+
+		boolean isAvailable = acMethodManager.isValidMethodAvailable(randomOres, null);
+
+		assertTrue(isAvailable);
+	}
+
+	@Test
+	public void testIsValidMethodAvailable_nonGui() {
+		//create a resource and an offer
+		OLATResource randomOres = createResource();
+		Offer offer = acService.createOffer(randomOres, "TestIsValidMethodAvailableNonGui");
+		offer = acService.save(offer);
+		dbInstance.commitAndCloseSession();
+
+		//create a link offer to method
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(ShibbolethAutoAccessMethod.class);
+		assertNotNull(methods);
+		assertEquals(1, methods.size());
+
+		AccessMethod method = methods.get(0);
+		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
+		acMethodManager.save(access);
+
+		dbInstance.commitAndCloseSession();
+
+		boolean isAvailable = acMethodManager.isValidMethodAvailable(randomOres, null);
+
+		assertFalse(isAvailable);
+	}
+
 	@Test
 	public void testOfferAccess() {
 		//create a resource and an offer
@@ -139,18 +200,18 @@ public class ACMethodManagerTest extends OlatTestCase {
 		Offer offer = acService.createOffer(randomOres, "TestOfferAccess");
 		offer = acService.save(offer);
 		dbInstance.commitAndCloseSession();
-		
+
 		//create a link offer to method
 		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
 		assertNotNull(methods);
 		assertEquals(1, methods.size());
-		
+
 		AccessMethod method = methods.get(0);
 		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
 		acMethodManager.save(access);
 
 		dbInstance.commitAndCloseSession();
-		
+
 		//retrieve the link
 		List<OfferAccess> retrievedOfferAccess = acMethodManager.getOfferAccess(offer, true);
 		assertNotNull(retrievedOfferAccess);
@@ -162,49 +223,49 @@ public class ACMethodManagerTest extends OlatTestCase {
 		assertNotNull(retrievedAccess.getOffer());
 		Assert.assertEquals(offer, retrievedAccess.getOffer());
 	}
-	
+
 	@Test
 	public void testSeveralOfferAccess() {
 		//create some resources and offers
 		OLATResource randomOres1 = createResource();
 		Offer offer1 = acService.createOffer(randomOres1, "TestSeveralOfferAccess 1");
 		offer1 = acService.save(offer1);
-		
+
 		OLATResource randomOres2 = createResource();
 		Offer offer2 = acService.createOffer(randomOres2, "TestSeveralOfferAccess 2");
 		offer2 = acService.save(offer2);
-		
+
 		OLATResource randomOres3 = createResource();
 		Offer offer3 = acService.createOffer(randomOres3, "TestSeveralOfferAccess 3");
 		offer3 = acService.save(offer3);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//create a link offer to method
 		List<AccessMethod> tokenMethods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
 		assertNotNull(tokenMethods);
 		assertEquals(1, tokenMethods.size());
 		AccessMethod tokenMethod = tokenMethods.get(0);
-		
+
 		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		assertNotNull(freeMethods);
 		assertEquals(1, freeMethods.size());
 		AccessMethod freeMethod = freeMethods.get(0);
-		
+
 		OfferAccess access1 = acMethodManager.createOfferAccess(offer1, tokenMethod);
 		acMethodManager.save(access1);
-		
+
 		OfferAccess access2 = acMethodManager.createOfferAccess(offer2, tokenMethod);
 		acMethodManager.save(access2);
-		
+
 		OfferAccess access3_1 = acMethodManager.createOfferAccess(offer3, tokenMethod);
 		acMethodManager.save(access3_1);
-		
+
 		OfferAccess access3_2 = acMethodManager.createOfferAccess(offer3, freeMethod);
 		acMethodManager.save(access3_2);
 
 		dbInstance.commitAndCloseSession();
-		
+
 		//retrieve the link to offer 1
 		List<OfferAccess> retrievedOfferAccess = acMethodManager.getOfferAccess(offer1, true);
 		assertNotNull(retrievedOfferAccess);
@@ -216,8 +277,8 @@ public class ACMethodManagerTest extends OlatTestCase {
 		assertNotNull(retrievedAccess.getOffer());
 		assertEquals(offer1, retrievedAccess.getOffer());
 		dbInstance.commitAndCloseSession();
-		
-		
+
+
 		{//retrieve the links to offer 3
 			List<OfferAccess> retrievedOfferAccess3 = acMethodManager.getOfferAccess(offer3, true);
 			assertNotNull(retrievedOfferAccess3);
@@ -244,16 +305,16 @@ public class ACMethodManagerTest extends OlatTestCase {
 			}
 			assertNotNull(retrievedAccess3_2.getOffer());
 			assertEquals(offer3, retrievedAccess3_2.getOffer());
-			
+
 			dbInstance.commitAndCloseSession();
 		}
-		
+
 		{//retrieve the links by resource
-			List<Offer> offers = new ArrayList<Offer>();
+			List<Offer> offers = new ArrayList<>();
 			offers.add(offer1);
 			offers.add(offer2);
 			offers.add(offer3);
-			
+
 			List<OfferAccess> retrievedAllOfferAccess = acMethodManager.getOfferAccess(offers, true);
 			assertNotNull(retrievedAllOfferAccess);
 			assertEquals(4, retrievedAllOfferAccess.size());
@@ -261,7 +322,7 @@ public class ACMethodManagerTest extends OlatTestCase {
 			dbInstance.commitAndCloseSession();
 		}
 	}
-	
+
 	@Test
 	public void testDeleteOfferAccess() {
 		//create some resources and offers
@@ -269,38 +330,38 @@ public class ACMethodManagerTest extends OlatTestCase {
 		Offer offer = acService.createOffer(randomOres1, "TestDeleteOfferAccess");
 		offer = acService.save(offer);
 		dbInstance.commitAndCloseSession();
-		
+
 		//create two link offer to method
 		List<AccessMethod> tokenMethods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
 		assertNotNull(tokenMethods);
 		assertEquals(1, tokenMethods.size());
 		AccessMethod tokenMethod = tokenMethods.get(0);
-		
+
 		List<AccessMethod> freeMethods = acMethodManager.getAvailableMethodsByType(FreeAccessMethod.class);
 		assertNotNull(freeMethods);
 		assertEquals(1, freeMethods.size());
 		AccessMethod freeMethod = freeMethods.get(0);
-		
-		
+
+
 		OfferAccess access3_1 = acMethodManager.createOfferAccess(offer, tokenMethod);
 		acMethodManager.save(access3_1);
-		
+
 		OfferAccess access3_2 = acMethodManager.createOfferAccess(offer, freeMethod);
 		acMethodManager.save(access3_2);
 
 		dbInstance.commitAndCloseSession();
-		
+
 		//delete one of them
 		acMethodManager.delete(access3_2);
 		dbInstance.commitAndCloseSession();
-		
-		//retrieve 
+
+		//retrieve
 		List<OfferAccess> retrievedOfferAccess = acMethodManager.getOfferAccess(offer, true);
 		assertNotNull(retrievedOfferAccess);
 		assertEquals(1, retrievedOfferAccess.size());
 		assertEquals(access3_1, retrievedOfferAccess.get(0));
 	}
-	
+
 	private OLATResource createResource() {
 		//create a repository entry
 		OLATResourceable resourceable = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
diff --git a/src/test/java/org/olat/resource/accesscontrol/ACOfferManagerTest.java b/src/test/java/org/olat/resource/accesscontrol/ACOfferManagerTest.java
index 350c0c29edb1d365fb288ebc5bea2dd6320c06d3..9587a02360d655ecd32c5d6ead57d1a18d8334e1 100644
--- a/src/test/java/org/olat/resource/accesscontrol/ACOfferManagerTest.java
+++ b/src/test/java/org/olat/resource/accesscontrol/ACOfferManagerTest.java
@@ -38,16 +38,19 @@ import org.olat.core.id.OLATResourceable;
 import org.olat.resource.OLATResource;
 import org.olat.resource.OLATResourceImpl;
 import org.olat.resource.OLATResourceManager;
+import org.olat.resource.accesscontrol.manager.ACMethodDAO;
 import org.olat.resource.accesscontrol.manager.ACOfferDAO;
+import org.olat.resource.accesscontrol.model.AccessMethod;
 import org.olat.resource.accesscontrol.model.OfferImpl;
+import org.olat.resource.accesscontrol.model.TokenAccessMethod;
 import org.olat.test.OlatTestCase;
 import org.springframework.beans.factory.annotation.Autowired;
 
 
 /**
- * 
+ *
  * Description:<br>
- * 
+ *
  * <P>
  * Initial Date:  18 avr. 2011 <br>
  * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
@@ -56,28 +59,31 @@ public class ACOfferManagerTest extends OlatTestCase {
 
 	@Autowired
 	private DB dbInstance;
-	
+
 	@Autowired
 	private ACOfferDAO acOfferManager;
-	
+
 	@Autowired
 	private ACService acService;
-	
+
+	@Autowired
+	private ACMethodDAO acMethodManager;
+
 	@Test
 	public void testManagers() {
 		assertNotNull(acOfferManager);
 		assertNotNull(acService);
 	}
-	
+
 	@Test
 	public void testSaveOffer() {
 		//create a resource
 		OLATResourceable testOreable = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable);
 		assertNotNull(testOres);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//create an offer
 		Offer offer = acOfferManager.createOffer(testOres, "TestSaveOffer");
 		assertNotNull(offer);
@@ -92,7 +98,15 @@ public class ACOfferManagerTest extends OlatTestCase {
 		acOfferManager.saveOffer(offer);
 
 		dbInstance.commitAndCloseSession();
-		
+
+		//create a link offer to method
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
+		AccessMethod method = methods.get(0);
+		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
+		acMethodManager.save(access);
+
+		dbInstance.commitAndCloseSession();
+
 		//check if the offer is saved
 		List<Offer> offers = acOfferManager.findOfferByResource(testOres, true, null);
 		assertNotNull(offers);
@@ -110,41 +124,49 @@ public class ACOfferManagerTest extends OlatTestCase {
 		assertEquals(testOres.getResourceableTypeName(), savedOffer.getResourceTypeName());
 		assertEquals("TestSaveOffer", savedOffer.getResourceDisplayName());
 	}
-	
+
 	@Test
 	public void testDeleteOffer() {
 		OLATResourceable testOreable = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable);
 		assertNotNull(testOres);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//create an offer
 		Offer offer = acOfferManager.createOffer(testOres, "TestDeleteOffer");
 		assertNotNull(offer);
 		assertEquals(OfferImpl.class, offer.getClass());
 		//and save the offer
 		acOfferManager.saveOffer(offer);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
+		//create a link offer to method
+		List<AccessMethod> methods = acMethodManager.getAvailableMethodsByType(TokenAccessMethod.class);
+		AccessMethod method = methods.get(0);
+		OfferAccess access = acMethodManager.createOfferAccess(offer, method);
+		acMethodManager.save(access);
+
+		dbInstance.commitAndCloseSession();
+
 		//retrieve the offer
 		List<Offer> offers = acOfferManager.findOfferByResource(testOres, true, null);
 		assertNotNull(offers);
 		assertEquals(1, offers.size());
 		assertEquals(offer, offers.get(0));
 		dbInstance.commitAndCloseSession();
-		
+
 		//delete the offer
 		acOfferManager.deleteOffer(offer);
 		dbInstance.commitAndCloseSession();
-		
+
 		//try to retrieve the offer
 		List<Offer> noOffers = acOfferManager.findOfferByResource(testOres, true, null);
 		assertNotNull(noOffers);
 		assertEquals(0, noOffers.size());
 		dbInstance.commitAndCloseSession();
-		
+
 		//retrieve all offers, deleted too
 		List<Offer> delOffers = acOfferManager.findOfferByResource(testOres, false, null);
 		assertNotNull(delOffers);
@@ -153,68 +175,68 @@ public class ACOfferManagerTest extends OlatTestCase {
 		assertEquals(false, delOffers.get(0).isValid());
 		dbInstance.commitAndCloseSession();
 	}
-	
+
 	@Test
 	public void testDeleteResource() {
 		//create a random resource
 		OLATResourceable testOreable = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable);
 		assertNotNull(testOres);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//create an offer
 		Offer offer = acOfferManager.createOffer(testOres, "TestDeleteResource");
 		assertNotNull(offer);
 		assertEquals(OfferImpl.class, offer.getClass());
 		//and save the offer
 		acOfferManager.saveOffer(offer);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//delete the resource
 		testOres = dbInstance.loadObject(OLATResourceImpl.class, testOres.getKey());
 		dbInstance.deleteObject(testOres);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//load offer by resource -> nothing found
 		List<Offer> retrievedOffers = acOfferManager.findOfferByResource(testOres, true, null);
 		assertNotNull(retrievedOffers);
 		assertEquals(0, retrievedOffers.size());
-		
+
 		//load offer by key -> found and loaded without error
 		Offer retrievedOffer = acOfferManager.loadOfferByKey(offer.getKey());
 		assertNotNull(retrievedOffer);
 		assertNull(retrievedOffer.getResource());
 		assertEquals(offer, retrievedOffer);
 	}
-	
+
 	@Test
 	public void testFilter() {
 		//create resources
 		OLATResourceable testOreable1 = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres1 = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable1);
 		assertNotNull(testOres1);
-		
+
 		OLATResourceable testOreable2 = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres2 = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable2);
 		assertNotNull(testOres2);
-		
+
 		OLATResourceable testOreable3 = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres3 = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable3);
 		assertNotNull(testOres3);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//create  offers
 		Offer offer1 = acOfferManager.createOffer(testOres1, "TestFilter 1");
 		Offer offer2 = acOfferManager.createOffer(testOres2, "TestFilter 2");
 		acOfferManager.saveOffer(offer1);
 		acOfferManager.saveOffer(offer2);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//filter by resources
 		List<Long> resourceKeys = new ArrayList<Long>();
 		resourceKeys.add(testOres1.getKey());
@@ -227,36 +249,36 @@ public class ACOfferManagerTest extends OlatTestCase {
 		assertTrue(filteredKeys.contains(testOres2.getKey()));
 		assertFalse(filteredKeys.contains(testOres3.getKey()));
 	}
-	
+
 	@Test
 	public void testFilterWithDelete() {
 		//create resources
 		OLATResourceable testOreable1 = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres1 = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable1);
 		assertNotNull(testOres1);
-		
+
 		OLATResourceable testOreable2 = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres2 = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable2);
 		assertNotNull(testOres2);
-		
+
 		OLATResourceable testOreable3 = new TypedResourceable(UUID.randomUUID().toString().replace("-", ""));
 		OLATResource testOres3 = OLATResourceManager.getInstance().findOrPersistResourceable(testOreable3);
 		assertNotNull(testOres3);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//create  offers
 		Offer offer1 = acOfferManager.createOffer(testOres1, "TestFilterWithDelete 1");
 		Offer offer2 = acOfferManager.createOffer(testOres2, "TestFilterWithDelete 2");
 		acOfferManager.saveOffer(offer1);
 		acOfferManager.saveOffer(offer2);
-		
+
 		dbInstance.commitAndCloseSession();
-		
+
 		//delete resource of offer 2
 		testOres2 = dbInstance.loadObject(OLATResourceImpl.class, testOres2.getKey());
 		dbInstance.deleteObject(testOres2);
-		
+
 		//filter by resources
 		List<Long> resourceKeys = new ArrayList<Long>();
 		resourceKeys.add(testOres1.getKey());
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/AdvanceOrderDAOTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/AdvanceOrderDAOTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f080c91294193a4f7750de9559016252bad6f7f1
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/AdvanceOrderDAOTest.java
@@ -0,0 +1,258 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.olat.core.commons.persistence.DB;
+import org.olat.core.id.Identity;
+import org.olat.resource.accesscontrol.manager.ACMethodDAO;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.model.FreeAccessMethod;
+import org.olat.resource.accesscontrol.model.TokenAccessMethod;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder.Status;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.olat.test.JunitTestHelper;
+import org.olat.test.OlatTestCase;
+import org.springframework.beans.factory.annotation.Autowired;
+
+/**
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class AdvanceOrderDAOTest extends OlatTestCase {
+
+	private static final IdentifierKey IDENTIFIER_KEY = IdentifierKey.externalId;
+	private static final String IDENTIFIER_VALUE = "identifierValue";
+	private Identity identity;
+	private AccessMethod freeMethod;
+	private AccessMethod tokenMethod;
+
+	@Autowired
+	private DB dbInstance;
+	@Autowired
+	private ACMethodDAO acMethodDAO;
+
+	@Autowired
+	private AdvanceOrderDAO sut;
+
+	@Before
+	public void emptyTable() {
+		String statement = "delete from advanceOrder";
+		dbInstance.getCurrentEntityManager().createQuery(statement).executeUpdate();
+	}
+
+	@Before
+	public void setUp() {
+		acMethodDAO.enableMethod(FreeAccessMethod.class, true);
+		List<AccessMethod> freeMethods = acMethodDAO.getAvailableMethodsByType(FreeAccessMethod.class);
+		freeMethod = freeMethods.get(0);
+		acMethodDAO.enableMethod(TokenAccessMethod.class, true);
+		List<AccessMethod> tokenMethods = acMethodDAO.getAvailableMethodsByType(TokenAccessMethod.class);
+		tokenMethod = tokenMethods.get(0);
+
+		identity = JunitTestHelper.createAndPersistIdentityAsRndUser("user");
+	}
+
+	@Test
+	public void shouldCreateAdvanceOrder() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+
+		assertThat(advanceOrder.getKey()).isNull();
+		assertThat(advanceOrder.getCreationDate()).isNotNull();
+		assertThat(advanceOrder.getLastModified()).isNotNull();
+		assertThat(advanceOrder.getIdentity()).isEqualTo(identity);
+		assertThat(advanceOrder.getIdentifierKey()).isEqualTo(IDENTIFIER_KEY);
+		assertThat(advanceOrder.getIdentifierValue()).isEqualTo(IDENTIFIER_VALUE);
+		assertThat(advanceOrder.getMethod()).isEqualTo(freeMethod);
+		assertThat(advanceOrder.getStatus()).isEqualTo(Status.PENDING);
+		assertThat(advanceOrder.getStatusModified()).isNotNull();
+	}
+
+	@Test
+	public void shouldSaveNewAdvanceOrder() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		dbInstance.commitAndCloseSession();
+
+		AdvanceOrder savedAdvanceOrder = sut.save(advanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		assertThat(savedAdvanceOrder.getKey()).isNotNull();
+	}
+
+	@Test
+	public void shouldSaveUpdatedAdvanceOrder() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		advanceOrder = sut.save(advanceOrder);
+		advanceOrder.setStatus(Status.DONE);
+
+		dbInstance.commitAndCloseSession();
+		AdvanceOrder savedAdvanceOrder = sut.save(advanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		assertThat(savedAdvanceOrder.getStatus()).isEqualTo(Status.DONE);
+	}
+
+	@Test
+	public void shouldFindPendingAdvanceOrderForIdentity() {
+		AdvanceOrder firstPendingAdvanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(firstPendingAdvanceOrder);
+		AdvanceOrder secondPendingAdvanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(secondPendingAdvanceOrder);
+		Identity otherIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("other");
+		AdvanceOrder advanceOrderWithOtherIdentity = sut.create(otherIdentity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(advanceOrderWithOtherIdentity);
+		AdvanceOrder doneAdvanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.accomplishAndSave(doneAdvanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		Collection<AdvanceOrder> pendingAdvanceOrders = sut.loadPendingAdvanceOrders(identity);
+
+		assertThat(pendingAdvanceOrders).hasSize(2);
+	}
+
+	@Test
+	public void shouldFindPendingAdvaceOrderForIdentifiers() {
+		AdvanceOrder aoMatchingInternalId = sut.create(identity, IdentifierKey.internalId, IDENTIFIER_VALUE, freeMethod);
+		sut.save(aoMatchingInternalId);
+		AdvanceOrder aoMatchingExternalId = sut.create(identity, IdentifierKey.externalId, IDENTIFIER_VALUE, freeMethod);
+		sut.save(aoMatchingExternalId);
+		AdvanceOrder aoNotMatchingRef = sut.create(identity, IdentifierKey.externalRef, IDENTIFIER_VALUE, freeMethod);
+		sut.save(aoNotMatchingRef);
+		AdvanceOrder aoNotMatchingValue = sut.create(identity, IdentifierKey.internalId, "not matching", freeMethod);
+		sut.save(aoNotMatchingValue);
+		dbInstance.commitAndCloseSession();
+
+		Map<IdentifierKey, String> identifiers = new HashMap<>();
+		identifiers.put(IdentifierKey.internalId, IDENTIFIER_VALUE);
+		identifiers.put(IdentifierKey.externalId, IDENTIFIER_VALUE);
+		Collection<AdvanceOrder> advanceOrders = sut.loadPendingAdvanceOrders(identifiers);
+
+		assertThat(advanceOrders).hasSize(2).contains(aoMatchingInternalId, aoMatchingExternalId);
+	}
+
+	@Test
+	public void shouldFindPendingAdvaceOrderForIdentifiersIfNullValues() {
+		AdvanceOrder aoMatchingInternalId = sut.create(identity, IdentifierKey.internalId, IDENTIFIER_VALUE, freeMethod);
+		sut.save(aoMatchingInternalId);
+		AdvanceOrder aoMatchingExternalId = sut.create(identity, IdentifierKey.externalId, IDENTIFIER_VALUE, freeMethod);
+		sut.save(aoMatchingExternalId);
+		AdvanceOrder aoNotMatchingRef = sut.create(identity, IdentifierKey.externalRef, IDENTIFIER_VALUE, freeMethod);
+		sut.save(aoNotMatchingRef);
+		AdvanceOrder aoNotMatchingValue = sut.create(identity, IdentifierKey.internalId, "not matching", freeMethod);
+		sut.save(aoNotMatchingValue);
+		dbInstance.commitAndCloseSession();
+
+		Map<IdentifierKey, String> identifiers = new HashMap<>();
+		identifiers.put(IdentifierKey.internalId, IDENTIFIER_VALUE);
+		identifiers.put(null, IDENTIFIER_VALUE);
+		identifiers.put(IdentifierKey.externalId, "");
+		Collection<AdvanceOrder> advanceOrders = sut.loadPendingAdvanceOrders(identifiers);
+
+		assertThat(advanceOrders).hasSize(1).contains(aoMatchingInternalId);
+	}
+
+	@Test
+	public void shouldMarkAsDoneWhenAccomplished() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		advanceOrder = sut.save(advanceOrder);
+
+		AdvanceOrder accomplishedAdvanceOrder = sut.accomplishAndSave(advanceOrder);
+
+		assertThat(accomplishedAdvanceOrder.getStatus()).isEqualTo(Status.DONE);
+	}
+
+	@Test
+	public void shouldNotMarkedAsDoneIfNoOffer() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		advanceOrder = sut.save(advanceOrder);
+
+		AdvanceOrder accomplishedAdvanceOrder = sut.accomplishAndSave(advanceOrder);
+
+		assertThat(accomplishedAdvanceOrder.getStatus()).isEqualTo(advanceOrder.getStatus());
+	}
+
+	@Test
+	public void shouldExistIfAllValuesTheSame() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(advanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		boolean exists = sut.exists(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+
+		assertThat(exists).isTrue();
+	}
+
+	@Test
+	public void shouldNotExistIfTheIdentityIsDifferent() {
+		Identity otherIdentity = JunitTestHelper.createAndPersistIdentityAsRndUser("other");
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(advanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		boolean exists = sut.exists(otherIdentity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+
+		assertThat(exists).isFalse();
+	}
+
+
+	@Test
+	public void shouldNotExistIfTheIdentifierKeyIsDifferent() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(advanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		boolean exists = sut.exists(identity, IdentifierKey.internalId, IDENTIFIER_VALUE, freeMethod);
+
+		assertThat(exists).isFalse();
+	}
+
+	@Test
+	public void shouldNotExistIfTheIdentifierValueIsDifferent() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(advanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		boolean exists = sut.exists(identity, IDENTIFIER_KEY, "otherValue", freeMethod);
+
+		assertThat(exists).isFalse();
+	}
+
+	@Test
+	public void shouldNotExistIfTheHandlerTypeIsDifferent() {
+		AdvanceOrder advanceOrder = sut.create(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, freeMethod);
+		sut.save(advanceOrder);
+		dbInstance.commitAndCloseSession();
+
+		boolean exists = sut.exists(identity, IDENTIFIER_KEY, IDENTIFIER_VALUE, tokenMethod);
+
+		assertThat(exists).isFalse();
+	}
+}
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/AutoAccessManagerImplTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/AutoAccessManagerImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..11e59deecaa50b6217f7af4ecf922a1ddd97a8ef
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/AutoAccessManagerImplTest.java
@@ -0,0 +1,282 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.isA;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.olat.basesecurity.GroupRoles;
+import org.olat.basesecurity.IdentityImpl;
+import org.olat.core.id.Identity;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.manager.RepositoryEntryRelationDAO;
+import org.olat.resource.OLATResource;
+import org.olat.resource.accesscontrol.ACService;
+import org.olat.resource.accesscontrol.AccessControlModule;
+import org.olat.resource.accesscontrol.Offer;
+import org.olat.resource.accesscontrol.OfferAccess;
+import org.olat.resource.accesscontrol.model.AccessMethod;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder.Status;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrderInput;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.olat.resource.accesscontrol.provider.auto.model.AdvanceOrderImpl;
+import org.olat.resource.accesscontrol.provider.auto.model.AutoAccessMethod;
+
+/**
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class AutoAccessManagerImplTest {
+
+	private static final Class<? extends AutoAccessMethod> METHOD_CLASS = AutoAccessMethod.class;
+	private static final IdentityImpl IDENTITY = new IdentityImpl();
+	private static final String FIRST_VALUE = "firstValue";
+	private static final String SECOND_VALUE = "secondvalue";
+	private static final String THIRD_VALUE = "third value";
+	private static final String DISPLAY_NAME = "displayName";
+
+	@Mock
+	private IdentifierHandler identifierHandlerMock;
+	@Mock
+	private InputValidator inputValidator;
+	@Mock
+	private SplitterFactory splitterFactory;
+	@Mock
+	private IdentifierValueSplitter splitterMock;
+	@Mock
+	private AdvanceOrderDAO advanceOrderDaoMock;
+	@Mock
+	private ACService acServiceMock;
+	@Mock
+	private AccessControlModule acModuleMock;
+	@Mock
+	private AdvanceOrderInput advanceOrderInputMock;
+	@Mock
+	private RepositoryEntryRelationDAO repositoryEntryRelationDaoMock;
+	@Mock
+	private RepositoryEntry repositoryEntryMock;
+	@Mock
+	private OLATResource resourceMock;
+
+	private AutoAccessMethod accessMethodDummy;
+
+	@InjectMocks
+	private AutoAccessManagerImpl sut = new  AutoAccessManagerImpl();
+
+	@Before
+	public void setUp() {
+		MockitoAnnotations.initMocks(this);
+
+		accessMethodDummy = mock(AutoAccessMethod.class);
+		List<AccessMethod> accessMethods = Arrays.asList(accessMethodDummy);
+		when(acServiceMock.getAvailableMethodsByType(AutoAccessMethod.class)).thenReturn(accessMethods);
+
+		doReturn(METHOD_CLASS).when(advanceOrderInputMock).getMethodClass();
+		when(advanceOrderInputMock.getIdentity()).thenReturn(IDENTITY);
+		Set<IdentifierKey> keys = new HashSet<>(Arrays.asList(IdentifierKey.internalId, IdentifierKey.externalId));
+		when(advanceOrderInputMock.getKeys()).thenReturn(keys);
+		when(advanceOrderInputMock.getSplitterType()).thenReturn("splitter");
+
+		when(acModuleMock.isAutoEnabled()).thenReturn(true);
+
+		when(inputValidator.isValid(advanceOrderInputMock)).thenReturn(true);
+
+		when(splitterFactory.getSplitter(anyString())).thenReturn(splitterMock);
+		List<String> values = Arrays.asList(FIRST_VALUE, SECOND_VALUE, THIRD_VALUE);
+		when(splitterMock.split(isNull())).thenReturn(values);
+
+		when(repositoryEntryMock.getDisplayname()).thenReturn(DISPLAY_NAME);
+		when(repositoryEntryMock.getOlatResource()).thenReturn(resourceMock);
+	}
+
+	private Collection<AdvanceOrder> getPendingAdvanceOrders() {
+		Collection<AdvanceOrder> advanceOrders;
+		AdvanceOrderImpl ao1 = new AdvanceOrderImpl();
+		ao1.setIdentifierKey(IdentifierKey.internalId);
+		ao1.setIdentifierValue("abc");
+		ao1.setStatus(Status.PENDING);
+		ao1.setMethod(accessMethodDummy);
+		ao1.setIdentity(IDENTITY);
+		AdvanceOrderImpl ao2 = new AdvanceOrderImpl();
+		ao2.setIdentifierKey(IdentifierKey.internalId);
+		ao2.setIdentifierValue("abc3");
+		ao2.setStatus(Status.PENDING);
+		ao2.setMethod(accessMethodDummy);
+		ao2.setIdentity(IDENTITY);
+		advanceOrders = Arrays.asList(ao1, ao2);
+		return advanceOrders;
+	}
+
+	@Test
+	public void shouldNotCreatAdvanceEntriesIfInputIsNotValid() {
+		when(inputValidator.isValid(advanceOrderInputMock)).thenReturn(false);
+
+		sut.createAdvanceOrders(advanceOrderInputMock);
+
+		verify(advanceOrderDaoMock, never()).create(any(Identity.class), any(IdentifierKey.class), anyString(), any(AutoAccessMethod.class));
+	}
+
+	@Test
+	public void shouldAddAnAdvanceEntryForEveryKeyValueCombination() {
+		sut.createAdvanceOrders(advanceOrderInputMock);
+
+		verify(advanceOrderDaoMock, times(6)).create(any(Identity.class), any(IdentifierKey.class), anyString(), any(AutoAccessMethod.class));
+	}
+
+	@Test
+	public void shouldAddAdvanceEntryOnlyIfNotExists() {
+		when(advanceOrderDaoMock.exists(IDENTITY, IdentifierKey.internalId, SECOND_VALUE, accessMethodDummy)).thenReturn(true);
+
+		sut.createAdvanceOrders(advanceOrderInputMock);
+
+		verify(advanceOrderDaoMock, times(5)).create(any(Identity.class), any(IdentifierKey.class), anyString(), any(AutoAccessMethod.class));
+	}
+
+	@Test
+	public void shouldNotGrantAccessIfAlreadyDone() {
+		Collection<AdvanceOrder> advanceOrders = new ArrayList<>();
+		AdvanceOrderImpl doneAdvanceOrder = new AdvanceOrderImpl();
+		doneAdvanceOrder.setStatus(Status.DONE);
+		doneAdvanceOrder.setIdentifierKey(IdentifierKey.externalId);
+		doneAdvanceOrder.setIdentifierValue("abc");
+		advanceOrders.add(doneAdvanceOrder);
+
+		sut.grantAccess(advanceOrders);
+
+		verify(identifierHandlerMock, never()).findRepositoryEntry(any(IdentifierKey.class), anyString());
+	}
+
+	@Test
+	public void shouldNotGrantAccessIfAutoAccessDisabled() {
+		when(acModuleMock.isAutoEnabled()).thenReturn(false);
+
+		sut.grantAccess(getPendingAdvanceOrders());
+
+		verify(identifierHandlerMock, never()).findRepositoryEntry(any(IdentifierKey.class), anyString());
+	}
+
+	@Test
+	public void shouldNotGrantAccessIfNoResourceFound() {
+		when(identifierHandlerMock.findRepositoryEntry(any(IdentifierKey.class), anyString())).thenReturn(null);
+
+		sut.grantAccess(getPendingAdvanceOrders());
+
+		verify(acServiceMock, never()).createOffer(any(OLATResource.class), anyString());
+		verify(acServiceMock, never()).createOfferAccess(any(Offer.class), any(AccessMethod.class));
+		verify(acServiceMock, never()).accessResource(any(Identity.class), any(OfferAccess.class), isNull());
+	}
+
+	@Test
+	public void shouldMakeOfferBeforeGrantingAccessIfNotExists() {
+		when(identifierHandlerMock.findRepositoryEntry(any(IdentifierKey.class), anyString())).thenReturn(repositoryEntryMock);
+		when(repositoryEntryRelationDaoMock.hasRole(IDENTITY, repositoryEntryMock, GroupRoles.participant.name())).thenReturn(false);
+		when(acServiceMock.getValidOfferAccess(any(OLATResource.class), any(AccessMethod.class))).thenReturn(new ArrayList<>());
+		Offer offerMock = mock(Offer.class);
+		when(acServiceMock.createOffer(any(OLATResource.class), anyString())).thenReturn(offerMock);
+		List<AccessMethod> methods = Arrays.asList(accessMethodDummy);
+		when(acServiceMock.getAvailableMethodsByType(METHOD_CLASS)).thenReturn(methods);
+
+		Collection<AdvanceOrder> advanceOrders = getPendingAdvanceOrders();
+		sut.grantAccess(advanceOrders);
+
+		verify(acServiceMock, times(2)).createOffer(any(OLATResource.class), anyString());
+		verify(acServiceMock, times(2)).createOfferAccess(any(Offer.class), isA(AccessMethod.class));
+	}
+
+	@Test
+	public void shouldNotMakeOfferBeforeGrantingAccessIfOfferExists() {
+		when(identifierHandlerMock.findRepositoryEntry(any(IdentifierKey.class), anyString())).thenReturn(repositoryEntryMock);
+		OfferAccess offerAccessDummy = mock(OfferAccess.class);
+		List<OfferAccess> offerAccess = Arrays.asList(offerAccessDummy);
+		when(acServiceMock.getValidOfferAccess(any(OLATResource.class), any(AccessMethod.class))).thenReturn(offerAccess);
+
+		sut.grantAccess(getPendingAdvanceOrders());
+
+		verify(acServiceMock, never()).createOffer(any(OLATResource.class), isNull());
+		verify(acServiceMock, never()).createOfferAccess(any(Offer.class), any(AccessMethod.class));
+	}
+
+	@Test
+	public void shouldGrantAccessIfNoAccess() {
+		when(identifierHandlerMock.findRepositoryEntry(any(IdentifierKey.class), anyString())).thenReturn(repositoryEntryMock);
+		OfferAccess offerAccessDummy = mock(OfferAccess.class);
+		List<OfferAccess> offerAccess = Arrays.asList(offerAccessDummy);
+		when(acServiceMock.getValidOfferAccess(any(OLATResource.class), any(AccessMethod.class))).thenReturn(offerAccess);
+
+		sut.grantAccess(getPendingAdvanceOrders());
+
+		verify(acServiceMock, times(2)).accessResource(IDENTITY, offerAccessDummy, null);
+	}
+
+	@Test
+	public void shouldNotGrantAccessIfHasAccess() {
+		when(identifierHandlerMock.findRepositoryEntry(any(IdentifierKey.class), anyString())).thenReturn(repositoryEntryMock);
+		when(repositoryEntryRelationDaoMock.hasRole(IDENTITY, repositoryEntryMock, GroupRoles.participant.name())).thenReturn(true);
+
+		sut.grantAccess(getPendingAdvanceOrders());
+
+		verify(acServiceMock, never()).accessResource(any(Identity.class), any(OfferAccess.class), isNull());
+	}
+
+	@Test
+	public void shouldMarkAccessOrderAsDone() {
+		when(identifierHandlerMock.findRepositoryEntry(any(IdentifierKey.class), anyString())).thenReturn(repositoryEntryMock);
+		OfferAccess offerAccessDummy = mock(OfferAccess.class);
+		List<OfferAccess> offerAccess = Arrays.asList(offerAccessDummy);
+		when(acServiceMock.getValidOfferAccess(any(OLATResource.class), any(AccessMethod.class))).thenReturn(offerAccess);
+
+		sut.grantAccess(getPendingAdvanceOrders());
+
+		verify(advanceOrderDaoMock, times(2)).accomplishAndSave(any(AdvanceOrder.class));
+	}
+
+	@Test
+	public void shouldFindAdvanceOrdersForEveryIdentifierKey() {
+		Long key = 1L;
+		when(resourceMock.getKey()).thenReturn(key);
+
+		sut.loadPendingAdvanceOrders(repositoryEntryMock);
+
+		verify(identifierHandlerMock, times(IdentifierKey.values().length)).getRepositoryEntryValue(any(IdentifierKey.class), any(RepositoryEntry.class));
+	}
+}
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalIdHandlerTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalIdHandlerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0d21fc8365c1c1d1f4954b3ff1b240fc2da3ec34
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalIdHandlerTest.java
@@ -0,0 +1,74 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
+
+/**
+ *
+ * Initial date: 15.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class ExternalIdHandlerTest {
+
+	@Mock
+	private RepositoryService repositoryServiceMock;
+	@Mock
+	private RepositoryEntry entryMock;
+
+	@InjectMocks
+	private ExternalIdHandler sut;
+
+	@Before
+	public void setUp() {
+		MockitoAnnotations.initMocks(this);
+	}
+
+	@Test
+	public void shouldDelegateTheSearchToRepositoryService() {
+		String externalId = "EXT-123";
+
+		sut.find(externalId);
+
+		verify(repositoryServiceMock).loadRepositoryEntriesByExternalId(externalId);
+	}
+
+	@Test
+	public void shouldReturnTheExternalIdFromRepositoryEntry() {
+		String externalId = "1234";
+		when(entryMock.getExternalId()).thenReturn(externalId);
+
+		String value = sut.getRepositoryEntryValue(entryMock);
+
+		assertThat(value).isEqualTo(externalId);
+	}
+
+}
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalRefHandlerTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalRefHandlerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..0eb900a40eb2c2952302a1e00094e76d4fdd7ab9
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/ExternalRefHandlerTest.java
@@ -0,0 +1,74 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
+
+/**
+ *
+ * Initial date: 15.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class ExternalRefHandlerTest {
+
+	@Mock
+	private RepositoryService repositoryServiceMock;
+	@Mock
+	private RepositoryEntry entryMock;
+
+	@InjectMocks
+	private ExternalRefHandler sut;
+
+	@Before
+	public void setUp() {
+		MockitoAnnotations.initMocks(this);
+	}
+
+	@Test
+	public void shouldDelegateTheSearchToRepositoryService() {
+		String externalRef = "EXT-123";
+
+		sut.find(externalRef);
+
+		verify(repositoryServiceMock).loadRepositoryEntriesByExternalRef(externalRef);
+	}
+
+	@Test
+	public void shouldReturnTheExternalRefFromRepositoryEntry() {
+		String externalRef = "1234";
+		when(entryMock.getExternalRef()).thenReturn(externalRef);
+
+		String value = sut.getRepositoryEntryValue(entryMock);
+
+		assertThat(value).isEqualTo(externalRef);
+	}
+
+}
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierHandlerTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierHandlerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1c2b0a419d873e10cf679fce65083d628b33fba9
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/IdentifierHandlerTest.java
@@ -0,0 +1,103 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.when;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import org.olat.repository.RepositoryEntry;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+
+import edu.emory.mathcs.backport.java.util.Collections;
+
+/**
+ *
+ * Initial date: 14.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ **/
+public class IdentifierHandlerTest {
+
+	private static RepositoryEntry course;
+	private static RepositoryEntry course2;
+
+	@Mock
+	private ExternalIdHandler externaHandlerMock;
+
+	@InjectMocks
+	private IdentifierHandler sut;
+
+	@Spy
+	private Collection<IdentifierKeyHandler> handlers = new ArrayList<>();
+
+	@Before
+	public void setUp() {
+		MockitoAnnotations.initMocks(this);
+
+		when(externaHandlerMock.getItentifierKey()).thenReturn(IdentifierKey.externalId);
+		handlers.add(externaHandlerMock);
+		sut.initHandlerCache();
+
+		 course = new RepositoryEntry();
+		 course2 = new RepositoryEntry();
+	}
+
+	@Test
+	public void shouldReturnTheRepositoryIfFoundOne() {
+		List<RepositoryEntry> resources = Arrays.asList(course);
+		when(externaHandlerMock.find(anyString())).thenReturn(resources);
+
+		RepositoryEntry loaded = sut.findRepositoryEntry(IdentifierKey.externalId, "EXT-123");
+
+		assertThat(loaded).isEqualTo(course);
+	}
+
+	@Test
+	public void shouldReturnNullIfNoCourseFound() {
+		when(externaHandlerMock.find(anyString())).thenReturn(Collections.<RepositoryEntry>emptyList());
+
+		RepositoryEntry loaded = sut.findRepositoryEntry(IdentifierKey.externalId, "EXT-123");
+
+		assertThat(loaded).isNull();
+	}
+
+	@Test
+	public void shouldReturnNullIfMoreThanOneCourseFound() {
+		List<RepositoryEntry> resources = Arrays.asList(course, course2);
+		when(externaHandlerMock.find(anyString())).thenReturn(resources);
+
+		RepositoryEntry loaded = sut.findRepositoryEntry(IdentifierKey.externalId, "EXT-123");
+
+		assertThat(loaded).isNull();
+	}
+
+}
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/InputValidatorTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/InputValidatorTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a828cbd122ccf9612dcc85e186accaf7cfe7bd6f
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/InputValidatorTest.java
@@ -0,0 +1,122 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.olat.basesecurity.IdentityImpl;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrderInput;
+import org.olat.resource.accesscontrol.provider.auto.IdentifierKey;
+import org.olat.resource.accesscontrol.provider.auto.model.AutoAccessMethod;
+
+/**
+ *
+ * Initial date: 17.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class InputValidatorTest {
+
+	private AdvanceOrderInput inputMock;
+
+	private InputValidator sut = new InputValidator();
+
+	@Before
+	public void setUp() {
+		inputMock = mock(AdvanceOrderInput.class);
+
+		when(inputMock.getIdentity()).thenReturn(new IdentityImpl());
+		Set<IdentifierKey> keys = new HashSet<>();
+		keys.add(IdentifierKey.externalId);
+		when(inputMock.getKeys()).thenReturn(keys);
+		when(inputMock.getRawValues()).thenReturn("rawValues");
+		doReturn(AutoAccessMethod.class).when(inputMock).getMethodClass();
+	}
+
+	@Test
+	public void shouldNotBeValidIfInputIsNull() {
+		boolean isValid = sut.isValid(null);
+
+		assertThat(isValid).isFalse();
+	}
+
+	@Test
+	public void shouldNotBeValidIfIdentityIsNull() {
+		when(inputMock.getIdentity()).thenReturn(null);
+
+		boolean isValid = sut.isValid(inputMock);
+
+		assertThat(isValid).isFalse();
+	}
+
+	@Test
+	public void shouldNotBeValidIfKeysIsNull() {
+		when(inputMock.getKeys()).thenReturn(null);
+
+		boolean isValid = sut.isValid(inputMock);
+
+		assertThat(isValid).isFalse();
+	}
+
+	@Test
+	public void shouldNotBeValidIfEmptyKeys() {
+		when(inputMock.getKeys()).thenReturn(new HashSet<>(0));
+
+		boolean isValid = sut.isValid(inputMock);
+
+		assertThat(isValid).isFalse();
+	}
+
+	@Test
+	public void shouldNotBeValidIfMethodtypeIsNull() {
+		when(inputMock.getMethodClass()).thenReturn(null);
+
+		boolean isValid = sut.isValid(inputMock);
+
+		assertThat(isValid).isFalse();
+	}
+
+	@Test
+	public void shouldNotBeValidIfRawValuesIsNull() {
+		when(inputMock.getRawValues()).thenReturn(null);
+
+		boolean isValid = sut.isValid(inputMock);
+
+		assertThat(isValid).isFalse();
+	}
+
+	@Test
+	public void shouldNotBeValidIfMethodDoesNotExist() {
+		when(inputMock.getIdentity()).thenReturn(null);
+
+		boolean isValid = sut.isValid(inputMock);
+
+		assertThat(isValid).isFalse();
+	}
+
+}
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/InternalIdHandlerTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/InternalIdHandlerTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ff4c8c89a1731cde50fdf96af71fe9d785b96d13
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/InternalIdHandlerTest.java
@@ -0,0 +1,84 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.olat.repository.RepositoryEntry;
+import org.olat.repository.RepositoryService;
+
+/**
+ *
+ * Initial date: 15.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class InternalIdHandlerTest {
+
+	@Mock
+	private RepositoryService repositoryServiceMock;
+	@Mock
+	private RepositoryEntry entryMock;
+
+	@InjectMocks
+	private InternalIdHandler sut;
+
+	@Before
+	public void setUp() {
+		MockitoAnnotations.initMocks(this);
+	}
+
+	@Test
+	public void shouldDelegateTheSearchToRepositoryService() {
+		sut.find("123");
+
+		verify(repositoryServiceMock).loadByKey(anyLong());
+	}
+
+	@Test
+	public void shouldReturnEmptyCollectionIfNotFound() {
+		when(repositoryServiceMock.loadByKey(anyLong())).thenReturn(null);
+
+		Collection<RepositoryEntry> resources = sut.find("123");
+
+		assertThat(resources).isEmpty();
+	}
+
+	@Test
+	public void shouldReturnTheExternalIdFromRepositoryEntry() {
+		Long key = 1234L;
+		when(entryMock.getKey()).thenReturn(key);
+
+		String value = sut.getRepositoryEntryValue(entryMock);
+
+		String keyAsString = Long.toString(key);
+		assertThat(value).isEqualTo(keyAsString);
+	}
+}
diff --git a/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/SemicolonSplitterTest.java b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/SemicolonSplitterTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..25e78ecd8c529000aadc6f1252b20d6d98c74e1a
--- /dev/null
+++ b/src/test/java/org/olat/resource/accesscontrol/provider/auto/manager/SemicolonSplitterTest.java
@@ -0,0 +1,72 @@
+/**
+ * <a href="http://www.openolat.org">
+ * OpenOLAT - Online Learning and Training</a><br>
+ * <p>
+ * Licensed under the Apache License, Version 2.0 (the "License"); <br>
+ * you may not use this file except in compliance with the License.<br>
+ * You may obtain a copy of the License at the
+ * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
+ * <p>
+ * Unless required by applicable law or agreed to in writing,<br>
+ * software distributed under the License is distributed on an "AS IS" BASIS, <br>
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
+ * See the License for the specific language governing permissions and <br>
+ * limitations under the License.
+ * <p>
+ * Initial code contributed and copyrighted by<br>
+ * frentix GmbH, http://www.frentix.com
+ * <p>
+ */
+package org.olat.resource.accesscontrol.provider.auto.manager;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.junit.Test;
+
+
+/**
+ *
+ * Initial date: 23.08.2017<br>
+ * @author uhensler, urs.hensler@frentix.com, http://www.frentix.com
+ *
+ */
+public class SemicolonSplitterTest {
+
+	private final static String SEMICOLON = ";";
+
+	private SemicolonSplitter sut = new SemicolonSplitter();
+
+	@Test
+	public void shouldReturnMultipleValues() {
+		String value1 = "single,3";
+		String value2 = "multiple";
+		String value3 = "value3";
+		String rawValue = value1 + SEMICOLON + value2 + SEMICOLON + value3 + SEMICOLON;
+		List<String> rawValues = Arrays.asList(value1, value2, value3);
+
+		Collection<String> values = sut.split(rawValue);
+
+		assertThat(values).hasSize(rawValues.size()).hasSameElementsAs(rawValues);
+	}
+
+	@Test
+	public void shouldReturnSingleValue() {
+		String singleValue = "single,3";
+		List<String> rawValues = Arrays.asList(singleValue);
+
+		Collection<String> values = sut.split(singleValue);
+
+		assertThat(values).hasSize(rawValues.size()).hasSameElementsAs(rawValues);
+	}
+
+	@Test
+	public void shouldReturnEmptyCollectionIfNullInput() {
+		Collection<String> values = sut.split(null);
+
+		assertThat(values).hasSize(0);
+	}
+}
diff --git a/src/test/java/org/olat/shibboleth/manager/ShibbolethAttributesTest.java b/src/test/java/org/olat/shibboleth/manager/ShibbolethAttributesTest.java
index a2c158d39c22e426bd221b18f4a36d1898f7a44e..4d041b79a3cdc4ba6d551de15a1a19f40ac074f1 100644
--- a/src/test/java/org/olat/shibboleth/manager/ShibbolethAttributesTest.java
+++ b/src/test/java/org/olat/shibboleth/manager/ShibbolethAttributesTest.java
@@ -69,6 +69,8 @@ public class ShibbolethAttributesTest {
 	private static final String SHIB_CITY_KEY = "shibCity";
 	private static final String SHIB_CITY_VALUE = null;
 	private static final String USER_OLD_VALUE = "old";
+	private static final String SHIB_AC_IDENTIFIER_KEY = "adIdenitfierKey";
+	private static final String SHIB_AC_IDENTIFIER_VALUE = "adIdenitfierValue";
 
 	@Mock
 	private ShibbolethModule shibbolethModuleMock;
@@ -93,6 +95,7 @@ public class ShibbolethAttributesTest {
 		when(shibbolethModuleMock.getShibbolethAttributeNames()).thenReturn(initShibbolethMap().keySet());
 		when(shibbolethModuleMock.getUIDAttributeName()).thenReturn(SHIB_UID_KEY);
 		when(shibbolethModuleMock.getPreferredLanguageAttributeName()).thenReturn(SHIB_LANG_KEY);
+		when(shibbolethModuleMock.getAcAutoAttributeName()).thenReturn(SHIB_AC_IDENTIFIER_KEY);
 
 		// ShibbolethAttributeHandler.parse() does not modify the value
 		ReflectionTestUtils.setField(sut, "shibbolethAttributeHandlerFactory", shibbolethAttributeHandlerFactoryMock);
@@ -114,6 +117,7 @@ public class ShibbolethAttributesTest {
 		shibbolethMap.put(SHIB_NAME_KEY, SHIB_NAME_VALUE);
 		shibbolethMap.put(SHIB_GENDER_KEY, SHIB_GENDER_VALUE);
 		shibbolethMap.put(SHIB_CITY_KEY, SHIB_CITY_VALUE);
+		shibbolethMap.put(SHIB_AC_IDENTIFIER_KEY, SHIB_AC_IDENTIFIER_VALUE);
 		return shibbolethMap;
 	}
 
@@ -137,7 +141,7 @@ public class ShibbolethAttributesTest {
 
 	@Test
 	public void shouldParseValuesWhenInit() {
-		verify(shibbolethAttributeHandlerMock, times(5)).parse(anyString());
+		verify(shibbolethAttributeHandlerMock, times(6)).parse(anyString());
 		verify(shibbolethAttributeHandlerMock, times(1)).parse(isNull());
 	}
 
@@ -155,6 +159,13 @@ public class ShibbolethAttributesTest {
 		assertThat(lang).isEqualTo(SHIB_LANG_VALUE);
 	}
 
+	@Test
+	public void shouldReturnAcIdentifierValues() {
+		String acName = sut.getAcRawValues();
+
+		assertThat(acName).isEqualTo(SHIB_AC_IDENTIFIER_VALUE);
+	}
+
 	@Test
 	public void shouldReplaceValueByUserPropertyName() {
 		String newValue = "newValue";
@@ -178,7 +189,7 @@ public class ShibbolethAttributesTest {
 	public void shouldNotParseValueWhenSet() {
 		sut.setValueForUserPropertyName(USER_NAME_KEY, "newValue");
 
-		verify(shibbolethAttributeHandlerMock, times(initUserMapping().size() + 1)).parse(anyString());
+		verify(shibbolethAttributeHandlerMock, times(initUserMapping().size() + 2)).parse(anyString());
 	}
 
 	@Test
diff --git a/src/test/java/org/olat/shibboleth/manager/ShibbolethManagerImplTest.java b/src/test/java/org/olat/shibboleth/manager/ShibbolethManagerImplTest.java
index 0d7b5d82b48210f2f315f248f880722333c90bc8..5dfaefe2ce8acf1f2de1ff41e357b87ab9a4c786 100644
--- a/src/test/java/org/olat/shibboleth/manager/ShibbolethManagerImplTest.java
+++ b/src/test/java/org/olat/shibboleth/manager/ShibbolethManagerImplTest.java
@@ -28,9 +28,11 @@ import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.Collection;
+import java.util.HashSet;
+
 import org.junit.Before;
 import org.junit.Test;
-import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.olat.basesecurity.BaseSecurity;
@@ -39,7 +41,11 @@ import org.olat.basesecurity.SecurityGroup;
 import org.olat.core.id.Identity;
 import org.olat.core.id.Preferences;
 import org.olat.core.id.User;
+import org.olat.resource.accesscontrol.AccessControlModule;
+import org.olat.resource.accesscontrol.provider.auto.AdvanceOrder;
+import org.olat.resource.accesscontrol.provider.auto.AutoAccessManager;
 import org.olat.shibboleth.ShibbolethDispatcher;
+import org.olat.shibboleth.ShibbolethModule;
 import org.olat.user.UserManager;
 import org.springframework.test.util.ReflectionTestUtils;
 
@@ -51,6 +57,10 @@ import org.springframework.test.util.ReflectionTestUtils;
  */
 public class ShibbolethManagerImplTest {
 
+	@Mock
+	private ShibbolethModule shibbolethModuleMock;
+	@Mock
+	private AccessControlModule acModuleMock;
 	@Mock
 	private BaseSecurity securityManagerMock;
 	@Mock
@@ -60,6 +70,10 @@ public class ShibbolethManagerImplTest {
 	@Mock
 	private UserManager userManagerMock;
 	@Mock
+	private AutoAccessManager autoAccessManagerMock;
+	@Mock
+	private ShibbolethAdvanceOrderInput advanceOrderInputMock;
+	@Mock
 	private Identity identityMock;
 	@Mock
 	private User userMock;
@@ -68,13 +82,16 @@ public class ShibbolethManagerImplTest {
 	@Mock
 	private ShibbolethAttributes attributesMock;
 
-	@InjectMocks
-	private ShibbolethManagerImpl sut;
+	private TestableShibbolethManagerImpl sut = new TestableShibbolethManagerImpl();
 
 	@Before
 	public void setUp() {
 		MockitoAnnotations.initMocks(this);
 		ReflectionTestUtils.setField(sut, "securityManager", securityManagerMock);
+		ReflectionTestUtils.setField(sut, "shibbolethModule", shibbolethModuleMock);
+		ReflectionTestUtils.setField(sut, "acModule", acModuleMock);
+		ReflectionTestUtils.setField(sut, "autoAccessManager", autoAccessManagerMock);
+		ReflectionTestUtils.setField(sut, "userManager", userManagerMock);
 
 		when(securityManagerMock.findSecurityGroupByName(Constants.GROUP_OLATUSERS))
 				.thenReturn(securityGroupOlatusersMock);
@@ -86,6 +103,7 @@ public class ShibbolethManagerImplTest {
 		when(identityMock.getUser()).thenReturn(userMock);
 		when(userMock.getPreferences()).thenReturn(preferencesMock);
 		when(attributesMock.syncUser(any(User.class))).then(returnsFirstArg());
+		when(attributesMock.getAcRawValues()).thenReturn("values");
 	}
 
 	@Test
@@ -130,6 +148,63 @@ public class ShibbolethManagerImplTest {
 		verify(securityManagerMock, never()).removeIdentityFromSecurityGroup(identityMock, securityGroupAuthorMock);
 	}
 
+	@Test
+	public void shouldCreateAdvanceOrderWhenCreating() {
+		when(acModuleMock.isAutoEnabled()).thenReturn(true);
+
+		sut.createUser(anyString(), anyString(), anyString(), attributesMock);
+
+		verify(autoAccessManagerMock).createAdvanceOrders(advanceOrderInputMock);
+	}
+
+	@Test
+	public void shouldBookAdvanceOrderWhenCreating() {
+		when(acModuleMock.isAutoEnabled()).thenReturn(true);
+
+		sut.createUser(anyString(), anyString(), anyString(), attributesMock);
+
+		verify(autoAccessManagerMock).grantAccessToCourse(identityMock);
+	}
+
+	@Test
+	public void shouldCreateAdvanceOrderWhenSyncing() {
+		when(acModuleMock.isAutoEnabled()).thenReturn(true);
+
+		sut.syncUser(identityMock, attributesMock);
+
+		verify(autoAccessManagerMock).createAdvanceOrders(advanceOrderInputMock);
+	}
+
+	@Test
+	public void shouldBookAdvanceOrderWhenSyncing() {
+		when(acModuleMock.isAutoEnabled()).thenReturn(true);
+
+		sut.syncUser(identityMock, attributesMock);
+
+		verify(autoAccessManagerMock).grantAccessToCourse(identityMock);
+	}
+
+
+	@Test
+	public void shouldNotCreateAdvanceOrderWhenDisabled() {
+		when(acModuleMock.isAutoEnabled()).thenReturn(false);
+
+		sut.syncUser(identityMock, attributesMock);
+
+		verify(autoAccessManagerMock, never()).createAdvanceOrders(null);
+	}
+
+	@Test
+	public void shouldNotBookAdvanceOrderWhenDisabled() {
+		when(acModuleMock.isAutoEnabled()).thenReturn(false);
+		Collection<AdvanceOrder> advanceOrders = new HashSet<>();
+		when(autoAccessManagerMock.loadPendingAdvanceOrders(identityMock)).thenReturn(advanceOrders);
+
+		sut.syncUser(identityMock, attributesMock);
+
+		verify(autoAccessManagerMock, never()).grantAccess(advanceOrders);
+	}
+
 	@Test
 	public void shouldSyncUserWhenAttributesChanged() {
 		when(attributesMock.hasDifference(userMock)).thenReturn(true);
@@ -155,6 +230,14 @@ public class ShibbolethManagerImplTest {
 		sut.syncUser(identityMock, attributesMock);
 
 		verify(userManagerMock, never()).updateUser(userMock);
+	}
+
+	private class TestableShibbolethManagerImpl extends ShibbolethManagerImpl {
+
+		@Override
+		protected ShibbolethAdvanceOrderInput getShibbolethAdvanceOrderInput() {
+			return advanceOrderInputMock;
+		}
 
 	}
 }
diff --git a/src/test/java/org/olat/test/AllTestsJunit4.java b/src/test/java/org/olat/test/AllTestsJunit4.java
index e610107a9d5fe07bea9dd1105b4b02c6f0ff3ffe..6d92861e6c96118befe98846bd9a70c50e100ff6 100644
--- a/src/test/java/org/olat/test/AllTestsJunit4.java
+++ b/src/test/java/org/olat/test/AllTestsJunit4.java
@@ -314,6 +314,7 @@ import org.junit.runners.Suite;
 	org.olat.resource.accesscontrol.ACOrderManagerTest.class,
 	org.olat.resource.accesscontrol.ACTransactionManagerTest.class,
 	org.olat.resource.accesscontrol.ACReservationDAOTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.AdvanceOrderDAOTest.class,
 	org.olat.core.util.vfs.version.VersionManagerTest.class,
 	/**
 	 * Pure JUnit test without need of framework
@@ -324,6 +325,13 @@ import org.junit.runners.Suite;
 	org.olat.modules.fo.WordCountTest.class,
 	org.olat.modules.webFeed.manager.FeedManagerImplTest.class,
 	org.olat.modules.webFeed.manager.RomeFeedFetcherTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.AutoAccessManagerImplTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.ExternalIdHandlerTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.ExternalRefHandlerTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.IdentifierHandlerTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.InputValidatorTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.InternalIdHandlerTest.class,
+	org.olat.resource.accesscontrol.provider.auto.manager.SemicolonSplitterTest.class,
 	org.olat.shibboleth.manager.DifferenceCheckerTest.class,
 	org.olat.shibboleth.manager.ShibbolethAttributesTest.class,
 	org.olat.shibboleth.manager.ShibbolethManagerImplTest.class,