GitLab wurde aktualisiert. Dank regelmäßiger Updates bleibt das THM GitLab sicher und Sie profitieren von den neuesten Funktionen. Vielen Dank für Ihre Geduld.

Commit dfa598b3 authored by Daniel Gerhardt's avatar Daniel Gerhardt
Browse files

Separate user persistance code and migrate it to Ektorp

parent 2a0b2945
......@@ -23,12 +23,15 @@ import com.fasterxml.jackson.databind.SerializationFeature;
import de.thm.arsnova.ImageUtils;
import de.thm.arsnova.connector.client.ConnectorClient;
import de.thm.arsnova.connector.client.ConnectorClientImpl;
import de.thm.arsnova.entities.DbUser;
import de.thm.arsnova.entities.LogEntry;
import de.thm.arsnova.entities.serialization.CouchDbDocumentModule;
import de.thm.arsnova.entities.serialization.CouchDbObjectMapperFactory;
import de.thm.arsnova.entities.serialization.View;
import de.thm.arsnova.persistance.LogEntryRepository;
import de.thm.arsnova.persistance.UserRepository;
import de.thm.arsnova.persistance.couchdb.CouchDbLogEntryRepository;
import de.thm.arsnova.persistance.couchdb.CouchDbUserRepository;
import de.thm.arsnova.persistance.couchdb.InitializingCouchDbConnector;
import de.thm.arsnova.socket.ARSnovaSocket;
import de.thm.arsnova.socket.ARSnovaSocketIOServer;
......@@ -273,6 +276,11 @@ public class AppConfig extends WebMvcConfigurerAdapter {
return new CouchDbLogEntryRepository(LogEntry.class, couchDbConnector(), false);
}
@Bean
public UserRepository userRepository() throws Exception {
return new CouchDbUserRepository(DbUser.class, couchDbConnector(), false);
}
@Bean(name = "connectorClient")
public ConnectorClient connectorClient() {
if (!connectorEnable) {
......
......@@ -2315,108 +2315,6 @@ public class CouchDBDao implements IDatabaseDao, ApplicationEventPublisherAware
return courseScore;
}
@Override
public DbUser createOrUpdateUser(DbUser user) {
try {
String id = user.getId();
String rev = user.getRev();
Document d = new Document();
if (null != id) {
d = database.getDocument(id, rev);
}
d.put("type", "userdetails");
d.put("username", user.getUsername());
d.put("password", user.getPassword());
d.put("activationKey", user.getActivationKey());
d.put("passwordResetKey", user.getPasswordResetKey());
d.put("passwordResetTime", user.getPasswordResetTime());
d.put("creation", user.getCreation());
d.put("lastLogin", user.getLastLogin());
database.saveDocument(d, id);
user.setId(d.getId());
user.setRev(d.getRev());
return user;
} catch (IOException e) {
logger.error("Could not save user {}.", user, e);
}
return null;
}
@Override
public DbUser getUser(String username) {
View view = new View("user/doc_by_username");
view.setKey(username);
ViewResults results = this.getDatabase().view(view);
if (results.getJSONArray("rows").optJSONObject(0) == null) {
return null;
}
return (DbUser) JSONObject.toBean(
results.getJSONArray("rows").optJSONObject(0).optJSONObject("value"),
DbUser.class
);
}
@Override
public boolean deleteUser(final DbUser dbUser) {
try {
this.deleteDocument(dbUser.getId());
log("delete", "type", "user", "id", dbUser.getId());
return true;
} catch (IOException e) {
logger.error("Could not delete user {}.", dbUser.getId(), e);
}
return false;
}
@Override
public int deleteInactiveUsers(long lastActivityBefore) {
try {
View view = new View("user/by_creation_for_inactive");
view.setEndKey(lastActivityBefore);
List<Document> results = this.getDatabase().view(view).getResults();
int count = 0;
final List<List<Document>> partitions = Lists.partition(results, BULK_PARTITION_SIZE);
for (List<Document> partition: partitions) {
final List<Document> newDocs = new ArrayList<>();
for (Document oldDoc : partition) {
final Document newDoc = new Document();
newDoc.setId(oldDoc.getId());
newDoc.setRev(oldDoc.getJSONObject("value").getString("_rev"));
newDoc.put("_deleted", true);
newDocs.add(newDoc);
logger.debug("Marked user document {} for deletion.", oldDoc.getId());
}
if (newDocs.size() > 0) {
if (getDatabase().bulkSaveDocuments(newDocs.toArray(new Document[newDocs.size()]))) {
count += newDocs.size();
}
}
}
if (count > 0) {
logger.info("Deleted {} inactive users.", count);
log("cleanup", "type", "user", "count", count);
}
return count;
} catch (IOException e) {
logger.error("Could not delete inactive users.", e);
}
return 0;
}
@Override
public SessionInfo importSession(User user, ImportExportSession importSession) {
final Session session = this.saveSession(user, importSession.generateSessionEntity(user));
......
......@@ -181,14 +181,6 @@ public interface IDatabaseDao {
int deleteAllQuestionsAnswers(Session session);
DbUser createOrUpdateUser(DbUser user);
DbUser getUser(String username);
boolean deleteUser(DbUser dbUser);
int deleteInactiveUsers(long lastActivityBefore);
CourseScore getLearningProgress(Session session);
List<SessionInfo> getMySessionsInfo(User user, final int start, final int limit);
......
......@@ -17,10 +17,13 @@
*/
package de.thm.arsnova.entities;
import com.fasterxml.jackson.annotation.JsonView;
import de.thm.arsnova.entities.serialization.View;
/**
* A user account for ARSnova's own registration and login process.
*/
public class DbUser {
public class DbUser implements Entity {
private String id;
private String rev;
private String username;
......@@ -31,90 +34,93 @@ public class DbUser {
private long creation;
private long lastLogin;
@JsonView(View.Persistence.class)
public String getId() {
return id;
}
@JsonView(View.Persistence.class)
public void setId(String id) {
this.id = id;
}
/* CouchDB deserialization */
public void set_id(String id) {
this.id = id;
}
@JsonView(View.Persistence.class)
public String getRev() {
return rev;
}
@JsonView(View.Persistence.class)
public void setRev(String rev) {
this.rev = rev;
}
/* CouchDB deserialization */
public void set_rev(String rev) {
this.rev = rev;
}
@JsonView({View.Persistence.class, View.Public.class})
public String getUsername() {
return username;
}
@JsonView({View.Persistence.class, View.Public.class})
public void setUsername(String username) {
this.username = username;
}
@JsonView(View.Persistence.class)
public String getPassword() {
return password;
}
@JsonView(View.Persistence.class)
public void setPassword(String password) {
this.password = password;
}
@JsonView(View.Persistence.class)
public String getActivationKey() {
return activationKey;
}
@JsonView(View.Persistence.class)
public void setActivationKey(String activationKey) {
this.activationKey = activationKey;
}
@JsonView(View.Persistence.class)
public String getPasswordResetKey() {
return passwordResetKey;
}
@JsonView(View.Persistence.class)
public void setPasswordResetKey(String passwordResetKey) {
this.passwordResetKey = passwordResetKey;
}
@JsonView(View.Persistence.class)
public long getPasswordResetTime() {
return passwordResetTime;
}
@JsonView(View.Persistence.class)
public void setPasswordResetTime(long passwordResetTime) {
this.passwordResetTime = passwordResetTime;
}
@JsonView(View.Persistence.class)
public long getCreation() {
return creation;
}
@JsonView(View.Persistence.class)
public void setCreation(long creation) {
this.creation = creation;
}
@JsonView(View.Persistence.class)
public long getLastLogin() {
return lastLogin;
}
@JsonView(View.Persistence.class)
public void setLastLogin(long lastLogin) {
this.lastLogin = lastLogin;
}
/* CouchDB deserialization */
public void setType(String type) {
/* no op */
}
}
......@@ -20,6 +20,7 @@ package de.thm.arsnova.entities.serialization;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.databind.util.Converter;
import de.thm.arsnova.entities.DbUser;
import de.thm.arsnova.entities.Entity;
import de.thm.arsnova.entities.LogEntry;
import java.util.HashMap;
......@@ -30,6 +31,7 @@ public class CouchDbTypeFieldConverter implements Converter<Class<? extends Enti
{
typeMapping.put(LogEntry.class, "log");
typeMapping.put(DbUser.class, "userdetails");
}
@Override
......
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.persistance;
import de.thm.arsnova.entities.DbUser;
public interface UserRepository {
DbUser findUserByUsername(String username);
DbUser createOrUpdateUser(DbUser user);
boolean deleteUser(DbUser user);
int deleteInactiveUsers(long lastActivityBefore);
}
/*
* This file is part of ARSnova Backend.
* Copyright (C) 2012-2017 The ARSnova Team
*
* ARSnova Backend is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* ARSnova Backend is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package de.thm.arsnova.persistance.couchdb;
import com.google.common.collect.Lists;
import de.thm.arsnova.entities.DbUser;
import de.thm.arsnova.persistance.UserRepository;
import org.ektorp.BulkDeleteDocument;
import org.ektorp.CouchDbConnector;
import org.ektorp.DocumentOperationResult;
import org.ektorp.ViewQuery;
import org.ektorp.ViewResult;
import org.ektorp.support.CouchDbRepositorySupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
public class CouchDbUserRepository extends CouchDbRepositorySupport<DbUser> implements UserRepository {
private static final int BULK_PARTITION_SIZE = 500;
private static final Logger logger = LoggerFactory.getLogger(CouchDbUserRepository.class);
public CouchDbUserRepository(Class<DbUser> type, CouchDbConnector db, boolean createIfNotExists) {
super(type, db, createIfNotExists);
}
private void log(Object... strings) {
/* TODO: method stub */
}
@Override
public DbUser createOrUpdateUser(final DbUser user) {
String id = user.getId();
if (null != id) {
db.update(user);
}
db.create(user);
return user;
}
@Override
public DbUser findUserByUsername(String username) {
List<DbUser> users = queryView("by_username", username);
return !users.isEmpty() ? users.get(0) : null;
}
@Override
public boolean deleteUser(final DbUser user) {
if (db.delete(user) != null) {
log("delete", "type", "user", "id", user.getId());
return true;
} else {
logger.error("Could not delete user {}", user.getId());
return false;
}
}
@Override
public int deleteInactiveUsers(long lastActivityBefore) {
ViewQuery q = createQuery("by_creation_for_inactive").endKey(lastActivityBefore);
List<ViewResult.Row> rows = db.queryView(q).getRows();
int count = 0;
final List<List<ViewResult.Row>> partitions = Lists.partition(rows, BULK_PARTITION_SIZE);
for (List<ViewResult.Row> partition: partitions) {
final List<BulkDeleteDocument> newDocs = new ArrayList<>();
for (ViewResult.Row oldDoc : partition) {
final BulkDeleteDocument newDoc = new BulkDeleteDocument(oldDoc.getId(), oldDoc.getValue());
newDocs.add(newDoc);
logger.debug("Marked user document {} for deletion.", oldDoc.getId());
}
if (newDocs.size() > 0) {
List<DocumentOperationResult> results = db.executeBulk(newDocs);
if (!results.isEmpty()) {
/* TODO: This condition should be improved so that it checks the operation results. */
count += newDocs.size();
}
}
}
if (count > 0) {
logger.info("Deleted {} inactive users.", count);
log("cleanup", "type", "user", "count", count);
}
return count;
}
}
......@@ -17,8 +17,8 @@
*/
package de.thm.arsnova.security;
import de.thm.arsnova.dao.IDatabaseDao;
import de.thm.arsnova.entities.DbUser;
import de.thm.arsnova.persistance.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -39,7 +39,7 @@ import java.util.List;
@Service
public class DbUserDetailsService implements UserDetailsService {
@Autowired
private IDatabaseDao dao;
private UserRepository userRepository;
private static final Logger logger = LoggerFactory
.getLogger(DbUserDetailsService.class);
......@@ -48,7 +48,7 @@ public class DbUserDetailsService implements UserDetailsService {
public UserDetails loadUserByUsername(String username) {
String uid = username.toLowerCase();
logger.debug("Load user: " + uid);
DbUser dbUser = dao.getUser(uid);
DbUser dbUser = userRepository.findUserByUsername(uid);
if (null == dbUser) {
throw new UsernameNotFoundException("User does not exist.");
}
......
......@@ -24,6 +24,7 @@ import de.thm.arsnova.entities.User;
import de.thm.arsnova.exceptions.BadRequestException;
import de.thm.arsnova.exceptions.NotFoundException;
import de.thm.arsnova.exceptions.UnauthorizedException;
import de.thm.arsnova.persistance.UserRepository;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.pac4j.oauth.profile.facebook.FacebookProfile;
......@@ -95,6 +96,9 @@ public class UserService implements IUserService {
/* used for Socket.IO online check solution (new) */
private static final ConcurrentHashMap<User, String> user2session = new ConcurrentHashMap<>();
@Autowired
private UserRepository userRepository;
@Autowired
private IDatabaseDao databaseDao;
......@@ -172,7 +176,7 @@ public class UserService implements IUserService {
logger.info("Delete inactive users.");
long unixTime = System.currentTimeMillis();
long lastActivityBefore = unixTime - ACTIVATION_KEY_DURABILITY_MS;
databaseDao.deleteInactiveUsers(lastActivityBefore);
userRepository.deleteInactiveUsers(lastActivityBefore);
}
@Override
......@@ -339,7 +343,7 @@ public class UserService implements IUserService {
@Override
public DbUser getDbUser(String username) {
return databaseDao.getUser(username.toLowerCase());
return userRepository.findUserByUsername(username.toLowerCase());
}
@Override
......@@ -360,7 +364,7 @@ public class UserService implements IUserService {
return null;
}
if (null != databaseDao.getUser(lcUsername)) {
if (null != userRepository.findUserByUsername(lcUsername)) {
logger.info("User registration failed. {} already exists.", lcUsername);
return null;
......@@ -372,7 +376,7 @@ public class UserService implements IUserService {
dbUser.setActivationKey(RandomStringUtils.randomAlphanumeric(32));
dbUser.setCreation(System.currentTimeMillis());
DbUser result = databaseDao.createOrUpdateUser(dbUser);
DbUser result = userRepository.createOrUpdateUser(dbUser);
if (null != result) {
sendActivationEmail(result);
} else {
......@@ -436,7 +440,7 @@ public class UserService implements IUserService {
@Override
public DbUser updateDbUser(DbUser dbUser) {
if (null != dbUser.getId()) {
return databaseDao.createOrUpdateUser(dbUser);
return userRepository.createOrUpdateUser(dbUser);
}
return null;
......@@ -456,7 +460,7 @@ public class UserService implements IUserService {
throw new NotFoundException();
}
databaseDao.deleteUser(dbUser);
userRepository.deleteUser(dbUser);
return dbUser;
}
......@@ -478,7 +482,7 @@ public class UserService implements IUserService {
dbUser.setPasswordResetKey(RandomStringUtils.randomAlphanumeric(32));
dbUser.setPasswordResetTime(System.currentTimeMillis());
if (null == databaseDao.createOrUpdateUser(dbUser)) {
if (null == userRepository.createOrUpdateUser(dbUser)) {
logger.error("Password reset failed. {} could not be updated.", username);
}
......
......@@ -422,36 +422,12 @@ public class StubDatabaseDao implements IDatabaseDao {
return 0;
}
@Override
public DbUser createOrUpdateUser(DbUser user) {
// TODO Auto-generated method stub
return null;
}
@Override
public DbUser getUser(String username) {
// TODO Auto-generated method stub
return null;
}
@Override
public CourseScore getLearningProgress(Session session) {
// TODO Auto-generated method stub
return null;
}
@Override
public boolean deleteUser(DbUser dbUser) {
// TODO Auto-generated method stub
return false;
}
@Override
public int deleteInactiveUsers(long lastActivityBefore) {
// TODO Auto-generated method stub
return 0;
}
@Override
public List<InterposedQuestion> getInterposedQuestions(Session session, User user, final int start, final int limit) {
// TODO Auto-generated method stub
......
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