Commit 533a43cb authored by Daniel Gerhardt's avatar Daniel Gerhardt

Remove implementation for Ilias

The Ilias connector implementation had an incompatible API and was never
usable with the ARSnova Backend.
parent d6e7e3ac
......@@ -13,7 +13,6 @@ We currently provide implementations to access Moodle, Ilias and Stud.IP:
| LMS | Retrieval method | Compatible versions |
|---------|--------------------|---------------------|
| Moodle | DBMS | * |
| Ilias | DBMS | * |
| Stud.IP | DBMS or REST API | * |
\* The database structures and/or APIs for the limited data accessed by LMS Connector usually do not change with new LMS versions and we do not have the resources to test against every new release. If you notice any incompatibilities, please create an issue.
......
......@@ -3,8 +3,6 @@ package de.thm.arsnova.connector.client;
import java.util.List;
import de.thm.arsnova.connector.model.Courses;
import de.thm.arsnova.connector.model.IliasCategoryNode;
import de.thm.arsnova.connector.model.IliasQuestion;
import de.thm.arsnova.connector.model.Membership;
/**
......@@ -36,12 +34,6 @@ import de.thm.arsnova.connector.model.Membership;
*
*/
public interface ConnectorClient {
public enum IliasQuestionSource {
RANDOM_TEST,
QUESTION_POOL
}
/** This service method returns the state of membership
*
* @param username The users name as used in target platform
......@@ -66,28 +58,4 @@ public interface ConnectorClient {
* @return The list of courses
*/
Courses getCourses(String username);
/** Returns a dump of the Ilias repository tree.
*
* @param refId The root nodes ID as reference ID
* @return A list of category nodes
*/
IliasCategoryNode getTreeObjects(int refId);
/** Returns a list of questions identified by the parent question pool reference ID
*
* The question source defaults to RANDOM_SOURCE
*
* @param refId The reference id of the question pool containing this question
* @return A list of questions containing the question, possible answers and feedback.
*/
List<IliasQuestion> getQuestions(int refId);
/** Returns a list of questions identified by the parent question pool reference ID
*
* @param refId The reference id of the question pool containing this question
* @param source The source of the questions
* @return A list of questions containing the question, possible answers and feedback.
*/
List<IliasQuestion> getQuestions(int refId, IliasQuestionSource source);
}
package de.thm.arsnova.connector.client;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.codec.binary.Base64;
import org.springframework.http.HttpEntity;
......@@ -12,8 +10,6 @@ import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import de.thm.arsnova.connector.model.Courses;
import de.thm.arsnova.connector.model.IliasCategoryNode;
import de.thm.arsnova.connector.model.IliasQuestion;
import de.thm.arsnova.connector.model.Membership;
public class ConnectorClientImpl implements ConnectorClient {
......@@ -21,8 +17,6 @@ public class ConnectorClientImpl implements ConnectorClient {
private static final String ISMEMBER_URI = "/{username}/membership/{courseid}";
private static final String GETCOURSES_URI = "/{username}/courses";
private static final String ILIAS_TREEOBJECTS_URI = "/ilias/{refid}";
private static final String ILIAS_QUESTIONS_URI = "/ilias/question/{refid}";
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
......@@ -72,46 +66,6 @@ public class ConnectorClientImpl implements ConnectorClient {
return response.getBody();
}
@Override
public IliasCategoryNode getTreeObjects(final int refId) {
final ResponseEntity<IliasCategoryNode> response = restTemplate.exchange(
buildRequestUri(ILIAS_TREEOBJECTS_URI),
HttpMethod.GET,
createNodeListEntity(),
IliasCategoryNode.class,
refId
);
return response.getBody();
}
@Override
public List<IliasQuestion> getQuestions(final int refId) {
final ResponseEntity<IliasQuestion[]> response = restTemplate.exchange(
buildRequestUri(ILIAS_QUESTIONS_URI),
HttpMethod.GET,
createQuestionListEntity(),
IliasQuestion[].class,
refId
);
return Arrays.asList(response.getBody());
}
@Override
public List<IliasQuestion> getQuestions(final int refId, final IliasQuestionSource source) {
final ResponseEntity<IliasQuestion[]> response = restTemplate.exchange(
buildRequestUri(ILIAS_QUESTIONS_URI + "?source={source}"),
HttpMethod.GET,
createQuestionListEntity(),
IliasQuestion[].class,
refId,
source
);
return Arrays.asList(response.getBody());
}
private HttpEntity<Membership> createMembershipEntity() {
return new HttpEntity<Membership>(getAuthorizationHeader());
}
......@@ -120,14 +74,6 @@ public class ConnectorClientImpl implements ConnectorClient {
return new HttpEntity<Courses>(getAuthorizationHeader());
}
private HttpEntity<List<IliasCategoryNode>> createNodeListEntity() {
return new HttpEntity<List<IliasCategoryNode>>(getAuthorizationHeader());
}
private HttpEntity<List<IliasQuestion>> createQuestionListEntity() {
return new HttpEntity<List<IliasQuestion>>(getAuthorizationHeader());
}
private HttpHeaders getAuthorizationHeader() {
final HttpHeaders httpHeaders = new HttpHeaders();
final String authorisation = httpUsername + ":" + httpPassword;
......
package de.thm.arsnova.connector.model;
public class IliasAnswer {
private String text;
private double points;
private double pointsUnchecked;
public IliasAnswer(String text, double points, double pointsUnchecked) {
this.text = text;
this.points = points;
this.pointsUnchecked = pointsUnchecked;
}
public String getText() {
return text;
}
public double getPoints() {
return points;
}
public double getPointsUnchecked() {
return pointsUnchecked;
}
}
package de.thm.arsnova.connector.model;
import java.util.ArrayList;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
@JsonInclude(Include.NON_NULL)
public class IliasCategoryNode {
private int id;
private int parent;
private String title;
private String type;
private Boolean leaf;
private int questionCount;
private Boolean isRandomTest;
private Integer randomQuestionAmount;
private List<IliasCategoryNode> children;
public IliasCategoryNode() {}
public IliasCategoryNode(final int id, final int parent, final String title, final String type, final int questionCount) {
this.id = id;
this.parent = parent;
this.title = title;
this.type = type;
this.questionCount = questionCount;
}
public int getId() {
return id;
}
public void setId(final int id) {
this.id = id;
}
public int getParent() {
return parent;
}
public void setParent(final int parent) {
this.parent = parent;
}
public String getTitle() {
return title;
}
public void setTitle(final String title) {
this.title = title;
}
public String getType() {
return type;
}
public void setType(final String type) {
this.type = type;
}
public int getQuestionCount() {
return questionCount;
}
public void setQuestionCount(final int questionCount) {
this.questionCount = questionCount;
}
public Boolean getIsRandomTest() {
return isRandomTest;
}
public void setIsRandomTest(final Boolean isRandomTest) {
this.isRandomTest = isRandomTest;
}
public Integer getRandomQuestionCount() {
return randomQuestionAmount;
}
public void setRandomQuestionCount(final Integer randomQuestionAmount) {
this.randomQuestionAmount = randomQuestionAmount;
}
public Boolean isLeaf() {
return leaf;
}
public void setLeaf(final Boolean leaf) {
this.leaf = leaf;
}
public List<IliasCategoryNode> getChildren() {
return children;
}
public void addChild(final IliasCategoryNode node) {
if (node == null) {
throw new IllegalArgumentException();
}
if (children == null) {
children = new ArrayList<IliasCategoryNode>();
}
children.add(node);
}
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (obj instanceof IliasCategoryNode) {
final IliasCategoryNode other = (IliasCategoryNode) obj;
return id == other.getId() && parent == other.getParent();
}
return false;
}
}
package de.thm.arsnova.connector.model;
public class IliasFeedback {
private boolean correctness;
private String feedback;
public IliasFeedback(String feedback, boolean correctness) {
this.correctness = correctness;
this.feedback = feedback;
}
public IliasFeedback() {
this.correctness = false;
this.feedback = null;
}
public String getFeedback() {
return feedback;
}
public void setFeedback(String feedback) {
this.feedback = feedback;
}
public boolean isCorrect() {
return correctness;
}
public void setCorrect(boolean isCorrect) {
this.correctness = isCorrect;
}
}
package de.thm.arsnova.connector.model;
import java.util.List;
public class IliasQuestion {
private int id;
private int type;
private double points;
private String title;
private String description;
private String text;
private int timestamp;
private List<IliasAnswer> answers;
private List<IliasFeedback> feedback;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public double getPoints() {
return points;
}
public void setPoints(double points) {
this.points = points;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public int getTimestamp() {
return timestamp;
}
public void setTimestamp(int timestamp) {
this.timestamp = timestamp;
}
public List<IliasAnswer> getAnswers() {
return answers;
}
public void setAnswers(List<IliasAnswer> answers) {
this.answers = answers;
}
public List<IliasFeedback> getFeedback() {
return feedback;
}
public void setFeedback(List<IliasFeedback> feedback) {
this.feedback = feedback;
}
}
......@@ -21,8 +21,6 @@ import org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;
import de.thm.arsnova.connector.dao.ConnectorDao;
import de.thm.arsnova.connector.dao.IliasConnectorDaoImpl;
import de.thm.arsnova.connector.dao.UniRepDao;
@ComponentScan(basePackages = {
"de.thm.arsnova.connector.dao",
......@@ -94,14 +92,6 @@ public class AppConfig {
return (ConnectorDao) Class.forName(env.getProperty("dao.implementation")).newInstance();
}
@Bean
public UniRepDao uniRepDao() throws InstantiationException, IllegalAccessException, ClassNotFoundException {
if ("enable".equals(env.getProperty("service.startIliasConnector"))) {
return new IliasConnectorDaoImpl();
}
return null;
}
private class HsqlDataSource extends DriverManagerDataSource {
@PreDestroy
public void shutdown() {
......
......@@ -14,9 +14,7 @@ import org.springframework.security.config.annotation.method.configuration.Enabl
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.servlet.configuration.EnableWebMvcSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.ldap.DefaultSpringSecurityContextSource;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import de.thm.arsnova.connector.auth.AuthenticationFilter;
import de.thm.arsnova.connector.auth.AuthenticationHandler;
......@@ -105,25 +103,6 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity http) throws Exception {
http.csrf().disable();
if("enable".equals(env.getProperty("service.startIliasConnector"))) {
http.authorizeRequests().antMatchers("/ilias/check").permitAll().and()
.authorizeRequests().antMatchers("/ilias/login").permitAll().and()
.authorizeRequests().anyRequest().authenticated().and()
.formLogin().loginPage("/ilias/login").usernameParameter("uname")
.passwordParameter("upass").successHandler(authHandler().authSuccessHandler())
.failureHandler(authHandler().authFailureHandler()).and()
.addFilterBefore(authFilter(),
UsernamePasswordAuthenticationFilter.class)
.exceptionHandling().authenticationEntryPoint(
authHandler().tokenAuthenticationEntryPoint()).and()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
} else {
http.httpBasic();
}
http.httpBasic();
}
}
......@@ -52,13 +52,6 @@ public class RepoPermissionEvaluator implements PermissionEvaluator {
return true;
}
break;
case "uniRepQuestion":
case "uniRepTree":
if ("read".equals(permission)) {
return true;
}
break;
}
return false;
......
package de.thm.arsnova.connector.dao;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import java.util.Map;
import de.thm.arsnova.connector.model.IliasCategoryNode;
import de.thm.arsnova.connector.model.IliasQuestion;
public interface UniRepDao {
/** Returns a flat dump of the Ilias repository tree.
*
* @param refId The root nodes ID as reference ID
* @return A list of category nodes
*/
List<IliasCategoryNode> getTreeObjects(int refId);
/** Returns a list of questions identified by the parent question pool reference ID
*
* @param refId The reference id of the question pool containing this question
* @return A list of questions containing the question, possible answers and feedback.
*/
List<IliasQuestion> getQuestion(int refId);
/** Returns a list of questions identified by the parent question pool reference ID from random tests
*
* @param refId The reference id of the question pool containing this question
* @return A list of questions containing the question, possible answers and feedback.
*/
List<IliasQuestion> getRandomTestQuestions(int refId);
/** Returns a map containing reference ids and the value of the meta data
*
* @param metaDataTitle The meta data to be returned
* @return A map with reference id - meta data value pairs
*/
Map<String, String> getReferenceIdsWithMetaDataFlag(String metaDataTitle);
/** Returns TRUE if referenced repository object is a test which is flagged as online
*
* @param refId The reference id of the test to be checked
* @return TRUE if this object is a test which is flagged as online
*/
boolean isTestOnline(int refId);
/** Returns TRUE if referenced repository object is a test with random question selection
*
* @param refId The reference id of the test to be checked
* @return TRUE if this object is a test with random question selection
*/
boolean isRandomQuestionSet(int refId);
/** Returns amount of questions in referenced test
*
* @param refId The reference id of the test to be checked
* @return Number of amounts per test
*/
int getQuestionAmountPerTest(int refId);
/** Marks classes implementing {@link UniRepDao} to filter results
*
* @author Paul-Christian Volkmer
* @since 0.50.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Filter {
/** Usable values for {@link Filter}
*/
public enum Type {
/** Filter to limit result set to question pools */
QUESTION_POOL,
/** Filter to limit result set to tests */
TEST
}
/** Declares wich kind of tree objects should be returned
*/
Type value();
}
}
package de.thm.arsnova.connector.services;
import java.util.List;
import org.springframework.security.access.prepost.PostAuthorize;
import de.thm.arsnova.connector.core.NotFoundException;
import de.thm.arsnova.connector.core.ServiceUnavailableException;
import de.thm.arsnova.connector.model.IliasCategoryNode;
import de.thm.arsnova.connector.model.IliasQuestion;
/** This service class provides access to ilias repository
*
* @author Paul-Christian Volkmer
* @since 0.20.0
*/
public interface UniRepService {
/** Returns a dump of the Ilias repository tree.
*
* @param refId The root nodes ID as reference ID
* @return A list of category nodes
* @throws ServiceUnavailableException
* @throws NotFoundException
*/
@PostAuthorize("isAuthenticated() and (hasRole('ADMIN') or hasPermission(#refId,'uniRepTree','read'))")
IliasCategoryNode getTreeObjects(int refId) throws ServiceUnavailableException, NotFoundException;
/** Returns a list of questions identified by the question pool or test reference ID
*
* @param refId The reference id of the question pool containing this question
* @return A list of questions containing the question, possible answers and feedback.
* @throws ServiceUnavailableException
*/
@PostAuthorize("isAuthenticated() and (hasRole('ADMIN') or hasPermission(#refId,'uniRepQuestion','read'))")
List<IliasQuestion> getQuestions(int refId, boolean noRandomQuestions) throws ServiceUnavailableException;
/** Returns a list of random questions identified by the test reference ID
*
* @param refId The reference id of the test containing this question
* @return A random list of questions containing the question, possible answers and feedback.
* @throws ServiceUnavailableException
*/
@PostAuthorize("isAuthenticated() and (hasRole('ADMIN') or hasPermission(#refId,'uniRepQuestion','read'))")
List<IliasQuestion> getRandomQuestions(int refId);
}
package de.thm.arsnova.connector.services;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import de.thm.arsnova.connector.core.NotFoundException;
import de.thm.arsnova.connector.core.ServiceUnavailableException;
import de.thm.arsnova.connector.dao.UniRepDao;
import de.thm.arsnova.connector.model.IliasCategoryNode;
import de.thm.arsnova.connector.model.IliasQuestion;
@Service
public class UniRepServiceImpl implements UniRepService {
@Autowired(required = false)
private UniRepDao uniRepDao;
@Override
public IliasCategoryNode getTreeObjects(final int refId)
throws ServiceUnavailableException, NotFoundException {
if (uniRepDao == null) {
throw new ServiceUnavailableException();
}
List<IliasCategoryNode> result = uniRepDao.getTreeObjects(refId);
result = removeNotMarkedNodes(result, false);
while (removeBranchesWithoutQuestionPools(result)) {
}
if (result.size() == 1) {
return result.get(0);
}
throw new NotFoundException();
}
@Override
public List<IliasQuestion> getQuestions(final int refId,
final boolean noRandomQuestions) throws ServiceUnavailableException {
if (uniRepDao == null) {
throw new ServiceUnavailableException();
}
if (uniRepDao.isRandomQuestionSet(refId) && !noRandomQuestions) {
return getRandomQuestions(refId);
} else {
return uniRepDao.getQuestion(refId);
}
}
@Override
public List<IliasQuestion> getRandomQuestions(final int refId) {
final int amountOfQuestions = uniRepDao.getQuestionAmountPerTest(refId);
final List<IliasQuestion> allQuestions = uniRepDao
.getRandomTestQuestions(refId);
final Set<Integer> randomIds = new HashSet<>();
while (randomIds.size() < amountOfQuestions) {
randomIds
.add((int) Math.floor(Math.random() * allQuestions.size()));
}
final List<IliasQuestion> result = new ArrayList<>();
for (final int id : randomIds) {
result.add(allQuestions.get(id));
}