Commit 9a30435d authored by Andreas Gärtner's avatar Andreas Gärtner

Merge branch 'master' of git@scm.thm.de:arsnova/connector.git

parents 9c670fee 86fb8ad9
......@@ -6,7 +6,7 @@ subprojects {
sourceCompatibility = 1.7
group = 'de.thm.arsnova.connector'
version = '0.71.0-SNAPSHOT'
version = '0.72.0-SNAPSHOT'
ext {
springVersion = '4.0.5.RELEASE'
......
......@@ -17,6 +17,7 @@ repositories {
dependencies {
compile project (':connector-model')
compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
compile group: 'commons-codec', name: 'commons-codec', version: '1.7'
compile group: 'org.springframework', name: 'spring-context', version: springVersion
compile group: 'org.springframework', name: 'spring-webmvc', version: springVersion
compile group: 'org.springframework', name: 'spring-jdbc', version: springVersion
......
......@@ -3,6 +3,8 @@ package de.thm.arsnova.connector.config;
import java.sql.SQLException;
import java.util.Properties;
import javax.annotation.PreDestroy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
......@@ -50,8 +52,8 @@ public class AppConfig {
}
@Bean(name = "configDataSource")
public DriverManagerDataSource configDataSource() throws SQLException {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
public HsqlDataSource configDataSource() throws SQLException {
HsqlDataSource dataSource = new HsqlDataSource();
dataSource.setDriverClassName("org.hsqldb.jdbc.JDBCDriver");
dataSource.setUrl("jdbc:hsqldb:file:/etc/arsnova/connector.db");
dataSource.setUsername("whatever");
......@@ -99,4 +101,16 @@ public class AppConfig {
}
return null;
}
private class HsqlDataSource extends DriverManagerDataSource {
@PreDestroy
public void shutdown() {
logger.info("Shutting down HSQLDB");
try {
this.getConnection().createStatement().execute("SHUTDOWN;");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
......@@ -44,9 +44,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
auth.inMemoryAuthentication().withUser(username)
.password(password).authorities("ADMIN");
auth.ldapAuthentication().contextSource(ldapContextSource())
.userSearchBase(ldapUserSearchBase)
.userSearchFilter(ldapUserSearchFilter);
if (!"".equals(ldapServerUrl)) {
auth.ldapAuthentication().contextSource(ldapContextSource())
.userSearchBase(ldapUserSearchBase)
.userSearchFilter(ldapUserSearchFilter);
}
}
@Bean
......@@ -80,7 +82,11 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public BaseLdapPathContextSource ldapContextSource() {
return new DefaultSpringSecurityContextSource(ldapServerUrl);
if (!"".equals(ldapServerUrl)) {
return new DefaultSpringSecurityContextSource(ldapServerUrl);
}
return null;
}
@Override
......
package de.thm.arsnova.connector.dao;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import org.apache.commons.codec.binary.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import de.thm.arsnova.connector.model.Course;
import de.thm.arsnova.connector.model.Membership;
import de.thm.arsnova.connector.model.UserRole;
import de.thm.arsnova.connector.model.studip_rest.StudipCourse;
import de.thm.arsnova.connector.model.studip_rest.StudipCoursesWrapper;
import de.thm.arsnova.connector.model.studip_rest.StudipUser;
import de.thm.arsnova.connector.model.studip_rest.StudipUsersWrapper;
public class StudipRestConnectorDaoImpl implements ConnectorDao {
private static final String TYPE = "studip";
private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
private static final Logger logger = LoggerFactory.getLogger(StudipRestConnectorDaoImpl.class);
private final RestTemplate restTemplate = new RestTemplate();
private String userMembershipUri = "/user/{userid}/courses";
private String userSearchUri = "/users?needle={username}";
@Value("${lms.http.serverUrl}") private String uriHostPart;
@Value("${lms.http.username}") private String httpUsername;
@Value("${lms.http.password}") private String httpPassword;
@PostConstruct
public void initialize() {
logger.info("Initializing {}", StudipRestConnectorDaoImpl.class.getName());
userMembershipUri = uriHostPart + userMembershipUri;
userSearchUri = uriHostPart + userSearchUri;
logger.debug("Using userMembershipUri={}, userSearchUri={}", userMembershipUri, userSearchUri);
}
@Override
public List<String> getCourseUsers(final String courseid) {
throw new UnsupportedOperationException();
}
@Override
public Membership getMembership(final String username, final String courseid) {
logger.trace("Retrieve membership status for user {} in course {}", username, courseid);
Membership membership = new Membership();
StudipUser user = getUserByName(username);
if (null == user) {
return membership;
}
try {
ResponseEntity<StudipCoursesWrapper> courseWrapperEntity = restTemplate.exchange(
userMembershipUri,
HttpMethod.GET,
new HttpEntity<Void>(getAuthorizationHeader()),
StudipCoursesWrapper.class,
user.getId()
);
List<StudipCourse> courses;
if (null != (courses = courseWrapperEntity.getBody().getStudy())) {
for (StudipCourse course : courses) {
if (courseid.equals(course.getId())) {
membership.setMember(true);
membership.setUserrole(UserRole.MEMBER);
logger.trace("User {} is a member of course {}", username, courseid);
return membership;
}
}
}
if (null != (courses = courseWrapperEntity.getBody().getWork())) {
for (StudipCourse course : courses) {
if (courseid.equals(course.getId())) {
membership.setMember(true);
membership.setUserrole(UserRole.TEACHER);
logger.trace("User {} is a teacher of course {}", username, courseid);
return membership;
}
}
}
} catch (RestClientException | HttpMessageNotReadableException e) {
/* Stud.IP sends an empty array instead of an object if no courses are found */
}
membership.setMember(false);
logger.trace("User {} is not a member of course {}", username, courseid);
return membership;
}
@Override
public List<Course> getMembersCourses(final String username) {
logger.trace("Retrieve courses for {}", username);
List<Course> courses = new ArrayList<>();
StudipUser user = getUserByName(username);
if (null == user) {
return courses;
}
try {
ResponseEntity<StudipCoursesWrapper> courseWrapperEntity = restTemplate.exchange(
userMembershipUri,
HttpMethod.GET,
new HttpEntity<Void>(getAuthorizationHeader()),
StudipCoursesWrapper.class,
user.getId()
);
List<StudipCourse> sipCourses;
if (null != (sipCourses = courseWrapperEntity.getBody().getStudy())) {
for (StudipCourse sipCourse : sipCourses) {
courses.add(buildCourse(sipCourse, UserRole.MEMBER));
}
}
if (null != (sipCourses = courseWrapperEntity.getBody().getWork())) {
for (StudipCourse sipCourse : sipCourses) {
courses.add(buildCourse(sipCourse, UserRole.TEACHER));
}
}
} catch (RestClientException | HttpMessageNotReadableException e) {
/* Stud.IP sends an empty array instead of an object if no courses are found */
}
return courses;
}
private StudipUser getUserByName(String username) {
logger.trace("Retrieve user {}", username);
try {
ResponseEntity<StudipUsersWrapper> usersWrapperEntity = restTemplate.exchange(
userSearchUri,
HttpMethod.GET,
new HttpEntity<Void>(getAuthorizationHeader()),
StudipUsersWrapper.class,
username
);
for (StudipUser user : usersWrapperEntity.getBody().getUsers()) {
if (username.equals(user.getUsername())) {
logger.trace("Username {} belongs to user {}", username, user);
return user;
}
}
logger.trace("No user was found for username {}", username);
} catch (RestClientException e) {
logger.error("Connector failed to retrieve user's ID from Stud.IP: {}", e.getMessage());
}
return null;
}
private HttpHeaders getAuthorizationHeader() {
HttpHeaders httpHeaders = new HttpHeaders();
String authorization = httpUsername + ":" + httpPassword;
httpHeaders.add(
"Authorization",
"Basic " + Base64.encodeBase64String(authorization.getBytes(UTF8_CHARSET))
);
return httpHeaders;
}
private Course buildCourse(StudipCourse sipCourse, UserRole role) {
Membership membership = new Membership();
membership.setMember(true);
membership.setUserrole(role);
Course course = new Course();
course.setId(sipCourse.getId());
course.setFullname(sipCourse.getTitle());
course.setShortname(sipCourse.getTitle());
course.setType(TYPE);
course.setMembership(membership);
return course;
}
}
package de.thm.arsnova.connector.model.studip_rest;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class StudipCourse {
private String id;
private String title;
private List<String> teachers;
private List<String> tutors;
private List<String> students;
public void setName(String name) {
this.title = name;
}
@JsonProperty("course_id")
public String getId() {
return id;
}
@JsonProperty("course_id")
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<String> getTeachers() {
return teachers;
}
public void setTeachers(List<String> teachers) {
this.teachers = teachers;
}
public List<String> getTutors() {
return tutors;
}
public void setTutors(List<String> tutors) {
this.tutors = tutors;
}
public List<String> getStudents() {
return students;
}
public void setStudents(List<String> students) {
this.students = students;
}
}
package de.thm.arsnova.connector.model.studip_rest;
import java.util.List;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class StudipCoursesWrapper {
private StudipCoursesInnerWrapper courses;
public StudipCoursesInnerWrapper getCourses() {
return courses;
}
public void setCourses(StudipCoursesInnerWrapper courses) {
this.courses = courses;
}
public List<StudipCourse> getStudy() {
return courses.study;
}
public void setStudy(List<StudipCourse> study) {
courses.study = study;
}
public List<StudipCourse> getWork() {
return courses.work;
}
public void setWork(List<StudipCourse> work) {
courses.work = work;
}
public static class StudipCoursesInnerWrapper {
private List<StudipCourse> study;
private List<StudipCourse> work;
public List<StudipCourse> getStudy() {
return study;
}
public void setStudy(List<StudipCourse> study) {
this.study = study;
}
public List<StudipCourse> getWork() {
return work;
}
public void setWork(List<StudipCourse> work) {
this.work = work;
}
}
}
package de.thm.arsnova.connector.model.studip_rest;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonIgnoreProperties(ignoreUnknown = true)
public class StudipUser {
private String id;
private String username;
@JsonProperty("user_id")
public String getId() {
return id;
}
@JsonProperty("user_id")
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
package de.thm.arsnova.connector.model.studip_rest;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class StudipUsersWrapper {
private StudipUser[] users;
public StudipUser[] getUsers() {
return users;
}
public void setUsers(StudipUser[] users) {
this.users = users;
}
}
......@@ -3,13 +3,23 @@ jdbc.url=jdbc:mysql://127.0.0.1:3306/moodle?zeroDateTimeBehavior=convertToNull
jdbc.username=root
jdbc.password=secret
lms.http.serverUrl=https://localhost/lms-api
lms.http.username=connector-user
lms.http.password=secret
# Select the data access object implementation your target platform
# Uncomment the following line to access Moodle
dao.implementation=de.thm.arsnova.connector.dao.MoodleConnectorDaoImpl
# Uncomment the following line to access Stud.IP
# dao.implementation=de.thm.arsnova.connector.dao.StudipConnectorDaoImpl
# Uncomment the following line to access Stud.IP via REST
# dao.implementation=de.thm.arsnova.connector.dao.StudipRestConnectorDaoImpl
# Uncomment the following line to get a dummy implementation
# dao.implementation=de.thm.arsnova.connector.dao.DummyConnectorDaoImpl
admin.username=test
admin.password=test
\ No newline at end of file
admin.password=test
ldap.serverUrl=
ldap.userSearchBase=
ldap.userSearchFilter=
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment