Commit ab44e677 authored by adam's avatar adam

Merge branch 'master' into dev/tests

parents b812fb1f d74fbd2d
......@@ -10,6 +10,7 @@ dbPort: 27017
dbName: db
flagHashMethod: "MD5"
salt: "SECURE_SALT"
admins:
- name: "gpietrus_admin"
......
package com.telephoners.krakyournet.ctf;
import com.bazaarvoice.dropwizard.webjars.WebJarBundle;
import com.google.common.collect.ImmutableSet;
import com.google.common.reflect.ClassPath;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.telephoners.krakyournet.ctf.auth.ExampleAuthenticator;
import com.telephoners.krakyournet.ctf.auth.UserAuthenticator;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.commands.PurgeDatabaseCommand;
import com.telephoners.krakyournet.ctf.commands.RegisterTasksCommand;
import com.telephoners.krakyournet.ctf.commands.RegisterTeamsCommand;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.logging.LoggingFilter;
import com.telephoners.krakyournet.ctf.modules.ApplicationModule;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.repositories.UsersRepository;
import com.telephoners.krakyournet.ctf.resources.*;
import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.Authorizer;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.jersey.setup.JerseyEnvironment;
import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import java.io.IOException;
import java.util.stream.Stream;
public class CTFApplication extends Application<ApplicationConfiguration>
......@@ -33,8 +34,7 @@ public class CTFApplication extends Application<ApplicationConfiguration>
@Override
public void initialize(final Bootstrap<ApplicationConfiguration> bootstrap)
{
//todo: refactor, will not work on artifact?
bootstrap.addBundle(new AssetsBundle("/assets_build", "/page", "index.html"));
bootstrap.addBundle(new AssetsBundle("/assets_build", "/", "index.html"));
bootstrap.addCommand(new PurgeDatabaseCommand());
bootstrap.addCommand(new RegisterTasksCommand());
bootstrap.addCommand(new RegisterTeamsCommand());
......@@ -43,26 +43,20 @@ public class CTFApplication extends Application<ApplicationConfiguration>
.forEach(bootstrap::addBundle);
}
private void registerResources(Environment environment)
private void registerResources(Environment environment) throws IOException
{
//todo: use reflections to iterate over resources
JerseyEnvironment jersey = environment.jersey();
jersey.register(injector.getInstance(TeamsResource.class));
jersey.register(injector.getInstance(TasksResource.class));
jersey.register(injector.getInstance(ProxyResource.class));
jersey.register(injector.getInstance(SolutionsResource.class));
jersey.register(injector.getInstance(TaskResource.class));
jersey.register(injector.getInstance(UtilResource.class));
final ClassPath classPath = ClassPath.from(this.getClass().getClassLoader());
ImmutableSet<ClassPath.ClassInfo> resourceClasses = classPath.getTopLevelClasses("com.telephoners.krakyournet.ctf.resources");
resourceClasses.stream()
.forEach(classInfo -> jersey.register(injector.getInstance(classInfo.load())));
}
private void registerAuthFeatures(Environment environment)
{
environment.jersey().register(new AuthDynamicFeature(
new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(injector.getInstance(ExampleAuthenticator.class))
.setAuthorizer(new ExampleAuthorizer())
.setRealm("SUPER SECRET STUFF")
//.setPrefix("Not-So-Basic")
.setAuthenticator(injector.getInstance(UserAuthenticator.class))
.buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
......@@ -89,16 +83,6 @@ public class CTFApplication extends Application<ApplicationConfiguration>
applicationConfiguration.getAdmins().forEach(usersRepository::add);
}
//todo: remove
public class ExampleAuthorizer implements Authorizer<User>
{
@Override
public boolean authorize(User user, String role)
{
return user.getName().equals("good-guy") && role.equals("ADMIN");
}
}
private Injector createInjector(ApplicationConfiguration applicationConfiguration)
{
return Guice.createInjector(new ApplicationModule(applicationConfiguration));
......
......@@ -8,13 +8,12 @@ import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials;
public class ExampleAuthenticator implements Authenticator<BasicCredentials, User>
public class UserAuthenticator implements Authenticator<BasicCredentials, User>
{
private UsersRepository usersRepository;
@Inject
public ExampleAuthenticator(UsersRepository usersRepository)
public UserAuthenticator(UsersRepository usersRepository)
{
this.usersRepository = usersRepository;
}
......
......@@ -5,6 +5,8 @@ import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import static com.google.common.base.Preconditions.checkNotNull;
@Entity("solutions")
public class Solution
{
......@@ -17,10 +19,10 @@ public class Solution
public Solution(Team team, Task task, Flag flag, String hashValue)
{
this.team = team;
this.task = task;
this.flag = flag;
this.hashValue = hashValue;
this.team = checkNotNull(team);
this.task = checkNotNull(task);
this.flag = checkNotNull(flag);
this.hashValue = checkNotNull(hashValue);
}
public Solution()
......
package com.telephoners.krakyournet.ctf.beans.tasks;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.helpers.PublicProperty;
import com.telephoners.krakyournet.ctf.beans.Flag;
import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id;
import java.io.IOException;
import java.util.List;
@Entity("tasks")
......@@ -32,6 +34,8 @@ public abstract class Task
{
}
public abstract String getTextForUser(User user) throws IOException;
public String getName()
{
return name;
......
......@@ -2,6 +2,7 @@ package com.telephoners.krakyournet.ctf.beans.tasks;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.User;
import org.mongodb.morphia.annotations.Entity;
import java.util.List;
......@@ -22,7 +23,7 @@ public class TextTask extends Task
{
}
public String getText()
public String getTextForUser(User user)
{
return text;
}
......
......@@ -2,8 +2,17 @@ package com.telephoners.krakyournet.ctf.beans.tasks;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.helpers.StreamUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.mongodb.morphia.annotations.Entity;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
@Entity("tasks")
......@@ -22,6 +31,11 @@ public class WebTask extends Task
{
}
public String getTextForUser(User user) throws IOException
{
return StreamUtils.readStream(proxyRequest(getUrl(), user));
}
public String getUrl()
{
return url;
......@@ -31,4 +45,14 @@ public class WebTask extends Task
{
this.url = url;
}
private InputStream proxyRequest(String url, User user) throws IOException
{
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(url);
httpget.setHeader("CTF-User", user.getName());
CloseableHttpResponse execute = httpClient.execute(httpget);
HttpEntity entity = execute.getEntity();
return entity.getContent();
}
}
......@@ -18,6 +18,7 @@ public class ApplicationConfiguration extends Configuration
private List<TextTask> textTasks;
private List<WebTask> webTasks;
private List<User> admins;
private String salt;
public List<User> getAdmins()
{
......@@ -98,4 +99,14 @@ public class ApplicationConfiguration extends Configuration
{
this.webTasks = webTasks;
}
public String getSalt()
{
return salt;
}
public void setSalt(String salt)
{
this.salt = salt;
}
}
package com.telephoners.krakyournet.ctf.core;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.providers.MessageDigestProvider;
import org.apache.commons.codec.binary.Hex;
@Singleton
public class HashValidator
{
private ApplicationConfiguration applicationConfiguration;
private MessageDigestProvider messageDigestProvider;
@Inject
public HashValidator(ApplicationConfiguration applicationConfiguration,
MessageDigestProvider messageDigestProvider)
{
this.applicationConfiguration = applicationConfiguration;
this.messageDigestProvider = messageDigestProvider;
}
public String calculateHashValue(User user, String flagValue)
{
String combinedStrings = applicationConfiguration.getSalt() + user.getName() + flagValue;
return Hex.encodeHexString(messageDigestProvider.getMessageDigest().digest(combinedStrings.getBytes()));
}
}
package com.telephoners.krakyournet.ctf.helpers;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
public class StreamUtils
{
public static String readStream(InputStream input) throws IOException
{
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
return buffer.lines().collect(Collectors.joining("\n"));
}
}
}
package com.telephoners.krakyournet.ctf.modules;
import com.google.inject.AbstractModule;
import com.mongodb.MongoClient;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.providers.DatastoreProvider;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
public class ApplicationModule extends AbstractModule
{
private ApplicationConfiguration applicationConfiguration;
public ApplicationModule(ApplicationConfiguration applicationConfiguration)
......@@ -19,14 +17,7 @@ public class ApplicationModule extends AbstractModule
@Override
protected void configure()
{
bind(ApplicationConfiguration.class).toInstance(applicationConfiguration); //todo: refactor
Morphia morphia = new Morphia();
morphia.mapPackage("beans.Team"); //todo? what for?
//todo: instantiate somewhere else?
Datastore datastore = morphia.createDatastore(
new MongoClient(applicationConfiguration.getDbHost(), applicationConfiguration.getDbPort()), applicationConfiguration.getDbName());
datastore.ensureIndexes();
bind(Datastore.class).toInstance(datastore);
bind(ApplicationConfiguration.class).toInstance(applicationConfiguration);
bind(Datastore.class).toInstance(new DatastoreProvider(applicationConfiguration).getDatastore());
}
}
package com.telephoners.krakyournet.ctf.providers;
import com.mongodb.MongoClient;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
import javax.inject.Inject;
public class DatastoreProvider
{
private ApplicationConfiguration applicationConfiguration;
@Inject
public DatastoreProvider(ApplicationConfiguration applicationConfiguration)
{
this.applicationConfiguration = applicationConfiguration;
}
public Datastore getDatastore()
{
Morphia morphia = new Morphia();
Datastore datastore = morphia.createDatastore(
new MongoClient(applicationConfiguration.getDbHost(), applicationConfiguration.getDbPort()), applicationConfiguration.getDbName());
datastore.ensureIndexes();
return datastore;
}
}
package com.telephoners.krakyournet.ctf.providers;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@Singleton
public class MessageDigestProvider
{
private ApplicationConfiguration applicationConfiguration;
@Inject
public MessageDigestProvider(ApplicationConfiguration applicationConfiguration)
{
this.applicationConfiguration = applicationConfiguration;
}
public MessageDigest getMessageDigest()
{
try {
return MessageDigest.getInstance(applicationConfiguration.getFlagHashMethod());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return null;
}
}
package com.telephoners.krakyournet.ctf.repositories;
import org.mongodb.morphia.Datastore;
import javax.inject.Inject;
import java.lang.reflect.ParameterizedType;
import java.util.List;
/**
* Created by gpietrus on 20.02.2016.
*/
public interface Repository {
// void getByTaskName(UUID uuid);
List getAll();
public abstract class Repository<T>
{
protected Datastore datastore;
@Inject
public Repository(Datastore datastore)
{
this.datastore = datastore;
}
public void add(T item)
{
datastore.save(item);
}
public List<T> getAll()
{
//todo: unchecked cast
return datastore.createQuery(getRepositoryType()).asList();
}
public void clean()
{
datastore.getCollection(getRepositoryType()).drop();
}
// void add(User user); //todo: not user //todo: use generics?
Class getRepositoryType()
{
try {
return Class.forName((((ParameterizedType) this.getClass().getGenericSuperclass())
.getActualTypeArguments()[0]).getTypeName());
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Class not found");
}
}
}
package com.telephoners.krakyournet.ctf.repositories;
import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.Solution;
import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.tasks.Task;
......@@ -10,47 +9,28 @@ import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Singleton
public class SolutionsRepository implements Repository
public class SolutionsRepository extends Repository<Solution>
{
private Datastore datastore;
private TasksRepository tasksRepository;
@Inject
public SolutionsRepository(Datastore datastore)
public SolutionsRepository(TasksRepository tasksRepository,
Datastore datastore)
{
this.datastore = datastore;
}
public List<Solution> getAll()
{
return datastore.createQuery(Solution.class).asList();
}
public void add(Solution solution)
{ //todo
datastore.save(solution); //todo: error handling?
//todo: do not add if already exists
}
public List<Solution> getByTeam(Team team)
{
//todo: merge with upper
//todo: use morphia filter
return datastore.createQuery(Solution.class)
.asList().stream()
.filter(solution -> solution.getTeam().equals(team))
.collect(Collectors.toList());
//todo: group by task
super(datastore);
this.tasksRepository = tasksRepository;
}
public Map<Integer, List<String>> getTeamSolutions(Team team)
{
return getByTeam(team).stream()
//todo: do not identify team by name? conflict in team ids?
return datastore.createQuery(Solution.class)
.filter("team.name",team.getName()).asList()
.stream()
.collect(Collectors.groupingBy(new Function<Solution, Task>()
{
@Override
......@@ -58,7 +38,9 @@ public class SolutionsRepository implements Repository
{
return solution1.getTask();
}
})).entrySet().stream()
}))
.entrySet()
.stream()
.collect(Collectors.toMap(
taskSolutions -> taskSolutions.getKey().getLevel(),
taskSolutions -> taskSolutions.getValue().stream()
......@@ -66,22 +48,31 @@ public class SolutionsRepository implements Repository
));
}
//todo: use datastore filter
public boolean exists(Solution solution)
public boolean isAlreadySubmittedSolution(Solution solution)
{
//todo: refactor, ugly ;(
String value = solution.getFlag().getValue();
Optional<Solution> matchedSolution = getAll().stream()
.filter(new Predicate<Solution>()
return datastore.find(Solution.class)
.filter("flag.value", solution.getFlag().getValue())
.get() != null;
}
public List<Integer> getCompletedTasks(Team team)
{
@Override
public boolean test(Solution solution)
Map<Integer, List<String>> teamSolutions = getTeamSolutions(team);
return tasksRepository.getAll().stream()
.filter(task -> {
List<String> teamTaskSolutions = teamSolutions.get(task.getLevel());
return teamTaskSolutions != null && teamTaskSolutions.size() == task.getFlags().size();
})
.map(Task::getLevel)
.collect(Collectors.toList());
}
public boolean submitSolution(Solution solution)
{
//todo: compare flags, not flag values
return solution.getFlag().getValue().equals(value);
if (!isAlreadySubmittedSolution(solution)) {
add(solution);
return true;
}
})
.findFirst();
return matchedSolution.isPresent();
return false;
}
}
\ No newline at end of file
package com.telephoners.krakyournet.ctf.repositories;
import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.Solution;
import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.beans.tasks.Task;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.core.HashValidator;
import com.telephoners.krakyournet.ctf.helpers.DBObjectUtils;
import javafx.util.Pair;
import org.apache.commons.codec.binary.Hex;
import org.mongodb.morphia.Datastore;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Singleton
public class TasksRepository implements Repository
public class TasksRepository extends Repository<Task>
{
private ApplicationConfiguration applicationConfiguration;
private Datastore datastore;
private TeamsRepository teamsRepository;
private SolutionsRepository solutionsRepository;
private String salt = "SECURE_SALT"; //todo: move to configuration!
private UsersRepository usersRepository;
private HashValidator hashValidator;
@Inject
public TasksRepository(ApplicationConfiguration applicationConfiguration, Datastore datastore,
TeamsRepository teamsRepository, SolutionsRepository solutionsRepository)
public TasksRepository(Datastore datastore,
UsersRepository usersRepository,
HashValidator hashValidator)
{
this.applicationConfiguration = applicationConfiguration;
super(datastore);
this.datastore = datastore;
this.teamsRepository = teamsRepository;
this.solutionsRepository = solutionsRepository;
this.usersRepository = usersRepository;
this.hashValidator = hashValidator;
}
public Task getByLevel(int level)
......@@ -49,16 +39,6 @@ public class TasksRepository implements Repository
.get();
}
private Optional<Task> getByUserFlag(String username, String flagValue)
{
return getUserFlagsHashes(username).entrySet()
.stream()
.filter(flagsMapEntry -> flagsMapEntry.getKey().contains(flagValue))
.map(Map.Entry::getValue)
.map(this::getByLevel)
.findFirst();
}
public List<Task> getAllPublic()
{
return datastore.createQuery(Task.class)
......@@ -66,135 +46,23 @@ public class TasksRepository implements Repository
.asList();
}
public List<Task> getAll()
public Map<Integer, List<String>> getUserFlagsHashes(String username)
{
return datastore.createQuery(Task.class).asList();
}
public void add(Task task)
{
datastore.save(task);
}
//todo: refactor?
public Map<List<String>, Integer> getUserFlagsHashes(String username)
{
return this.getAll().stream()
return getAll().stream()
.collect(Collectors.toMap(
task -> {
List<String> collect = task.getFlags().stream()
.map(flag -> calculateHashValue(username, flag.getValue()))
.collect(Collectors.toList());
return collect;
},
Task::getLevel
Task::getLevel,
task -> task.getFlags().stream()
.map(flag -> hashValidator.calculateHashValue(usersRepository.getUserByName(username), flag.getValue()))
.collect(Collectors.toList())
));
}
//todo: refactor with the function below
private Optional<Pair<Task, Flag>> getTaskFlagPairByHashValue(User user, String userHash)
{
String username = user.getName();
//todo: collapse lambdas
Optional<Pair<Task, Flag>> matched = this.getAll().stream()
.collect(Collectors.toMap(
task -> task,
Task::getFlags
))
.entrySet()
.stream()
.map((Function<Map.Entry<Task, List<Flag>>, Pair<Task, Optional<Flag>>>) taskFlagsEntry -> {
Task task = taskFlagsEntry.getKey();
Optional<Flag> matchedFlag = taskFlagsEntry.getValue().stream()
.filter(new Predicate<Flag>()
{
@Override
public boolean test(Flag flag1)
{
return calculateHashValue(username, flag1.getValue()).equals(userHash);
}
})
.findFirst();
return new Pair<Task, Optional<Flag>>(task, matchedFlag);
})
.filter(new Predicate<Pair<Task, Optional<Flag>>>()
{
@Override
public boolean test(Pair<Task, Optional<Flag>> taskOptionalPair)
{
return taskOptionalPair.getValue().isPresent();
}
})
.map(new Function<Pair<Task, Optional<Flag>>, Pair<Task, Flag>>()
{
@Override
public Pair<Task, Flag> apply(Pair<Task, Optional<Flag>> taskOptionalPair)
{
return new Pair<Task, Flag>(taskOptionalPair.getKey(), taskOptionalPair.getValue().get());
}
})
.findFirst();
return matched;
//todo: refactor
}
public String calculateHashValue(String username, String flagValue)
{ //todo
String combinedStrings = salt + username + flagValue; //todo
MessageDigest md5 = null;//todo: discuss
try {
md5 = MessageDigest.getInstance(applicationConfiguration.getFlagHashMethod());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
String encodedHash = Hex.encodeHexString(md5.digest(combinedStrings.getBytes()));
return encodedHash;
}
private boolean isAlreadySubmittedSolution(Solution solution)
public Pair<Task, Flag> getTaskFlagPairByHashValue(User user, String userHash, int taskLevel)
{
return solutionsRepository.exists(solution);
}
public boolean checkHash(User user, String hashValue)
{
//todo: refactor
Optional<Pair<Task, Flag>> taskFlagPairOptional = getTaskFlagPairByHashValue(user, hashValue);
if (!taskFlagPairOptional.isPresent()) {
return false;
}
Pair<Task, Flag> taskFlagPair = taskFlagPairOptional.get();
Task task = taskFlagPair.getKey();
Flag flag = taskFlagPair.getValue();
Optional<Team> team = teamsRepository.getTeamByUser(user);
if (team.isPresent()) {
//todo: combine ifs
Solution solution = new Solution(team.get(), task, flag, hashValue);
if (!isAlreadySubmittedSolution(solution)) {
solutionsRepository.add(solution);
return true;
}
}
return false;
}
public void clean()
{
datastore.getCollection(Task.class).drop();
}
//todo: should it be here?
public List<Integer> getCompletedTasks(Team team)
{
Map<Integer, List<String>> teamSolutions = solutionsRepository.getTeamSolutions(team);
return getAll().stream()
.filter(task -> {
int numberOfFlags = task.getFlags().size();
List<String> teamTaskSolutions = teamSolutions.get(task.getLevel());
return teamTaskSolutions != null && teamTaskSolutions.size() == numberOfFlags;
})
.map(Task::getLevel)
.collect(Collectors.toList());
Flag matchedFlag = getByLevel(taskLevel).getFlags().stream()
.filter(flag -> hashValidator.calculateHashValue(user, flag.getValue()).equals(userHash))
.findFirst()
.get();
return new Pair<>(getByLevel(taskLevel), matchedFlag);
}
}
\ No newline at end of file
package com.telephoners.krakyournet.ctf.repositories;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.User;
import org.mongodb.morphia.Datastore;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Created by gpietrus on 20.02.2016.
*/
@Singleton
public class TeamsRepository implements Repository
public class TeamsRepository extends Repository<Team>
{
private Datastore datastore;
private ApplicationConfiguration applicationConfiguration;
private UsersRepository usersRepository;
@Inject
public TeamsRepository(Datastore datastore, ApplicationConfiguration applicationConfiguration,
public TeamsRepository(Datastore datastore,
UsersRepository usersRepository)
{
super(datastore);
this.datastore = datastore;
this.applicationConfiguration = applicationConfiguration;
this.usersRepository = usersRepository;
}
public Optional<Team> getTeamByUser(User user)
public Team getTeamByUser(User user)
{
return datastore.createQuery(Team.class).asList().stream()
.filter(team -> team.getMembers().contains(user))
.findFirst();
.findFirst().get();
}
public Optional<Team> getTeamByUserName(String username)
public Team getTeamByUserName(String username)
{
return getTeamByUser(usersRepository.getUserByName(username));
}
public void get(UUID uuid)
{
}
public List<Team> getAll()
{
return datastore.createQuery(Team.class).asList();
}
//todo: move to interface
//todo: use default as interface-implemented methods
public void add(Team team)
{
datastore.save(team);
}
//todo: move clean to upper class?
public void clean()
{
datastore.getCollection(Team.class).drop();
}
}
......@@ -3,34 +3,21 @@ package com.telephoners.krakyournet.ctf.repositories;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.providers.MessageDigestProvider;
import io.dropwizard.auth.basic.BasicCredentials;
import org.apache.commons.codec.binary.Hex;
import org.mongodb.morphia.Datastore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
@Singleton
public class UsersRepository implements Repository
public class UsersRepository extends Repository<User>
{
private Datastore datastore;
private MessageDigest messageDigest;
private MessageDigestProvider messageDigestProvider;
@Inject
public UsersRepository(Datastore datastore)
{
this.datastore = datastore;
try {
messageDigest = MessageDigest.getInstance("MD5"); //todo
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public void add(User user)
public UsersRepository(Datastore datastore, MessageDigestProvider messageDigestProvider)
{
datastore.save(user);
super(datastore);
this.messageDigestProvider = messageDigestProvider;
}
public User getUserByName(String username)
......@@ -44,13 +31,7 @@ public class UsersRepository implements Repository
{
return datastore.createQuery(User.class)
.field("name").equal(basicCredentials.getUsername())
.field("password").equal(Hex.encodeHexString(messageDigest.digest(basicCredentials.getPassword().getBytes())))
.field("password").equal(Hex.encodeHexString(messageDigestProvider.getMessageDigest().digest(basicCredentials.getPassword().getBytes())))
.get();
}
@Override
public List getAll()
{
return null;
}
}
package com.telephoners.krakyournet.ctf.resources;
import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.Solution;
import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.beans.tasks.Task;
import com.telephoners.krakyournet.ctf.core.HashValidator;
import com.telephoners.krakyournet.ctf.repositories.SolutionsRepository;
import com.telephoners.krakyournet.ctf.repositories.TasksRepository;
import com.telephoners.krakyournet.ctf.repositories.TeamsRepository;
import io.dropwizard.auth.Auth;
import javafx.util.Pair;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
@Singleton
......@@ -34,17 +35,22 @@ public class SolutionsResource
TeamsRepository teamsRepository)
{
this.solutionsRepository = solutionsRepository;
this.tasksRepository = tasksRepository;
this.teamsRepository = teamsRepository;
this.tasksRepository = tasksRepository;
}
@POST
@Path("/{task_level}")
public Response submitSolution(@Auth User user,
@PathParam("task_level") int taskLevel,
String hash) throws Exception
{
if (tasksRepository.checkHash(user, hash)) {
Pair<Task, Flag> taskFlagPair = tasksRepository.getTaskFlagPairByHashValue(user, hash, taskLevel);
if (taskFlagPair != null) {
if(solutionsRepository.submitSolution(new Solution(teamsRepository.getTeamByUser(user), taskFlagPair.getKey(), taskFlagPair.getValue(), hash))) {
return Response.ok().build();
}
}
return Response.status(Response.Status.NOT_ACCEPTABLE).build();
}
......@@ -52,8 +58,7 @@ public class SolutionsResource
@Path("/completed")
public List<Integer> getTeamCompletedTasks(@Auth User user)
{
Optional<Team> team = teamsRepository.getTeamByUser(user);
return tasksRepository.getCompletedTasks(team.get());
return solutionsRepository.getCompletedTasks(teamsRepository.getTeamByUser(user));
}
@GET
......@@ -64,7 +69,7 @@ public class SolutionsResource
.stream()
.collect(Collectors.toMap(
Team::getName,
team -> tasksRepository.getCompletedTasks(team)
team -> solutionsRepository.getCompletedTasks(team)
));
}
......@@ -72,13 +77,12 @@ public class SolutionsResource
@Path("/my")
public Map<Integer, List<String>> getTeamSolutions(@Auth User user)
{
Optional<Team> team = teamsRepository.getTeamByUser(user);
return solutionsRepository.getTeamSolutions(team.get());
Team team = teamsRepository.getTeamByUser(user);
return solutionsRepository.getTeamSolutions(team);
}
@GET
@Path("/all")
//todo: should return completed, not all flags?
public Map<String, Map<Integer, List<String>>> getTeamsSolutions()
{
return teamsRepository.getAll()
......
......@@ -46,36 +46,6 @@ public class TaskResource
if (task == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
String taskText = null;
//todo: refactor, so ugly;(
if(task instanceof WebTask) {
taskText = readStream(proxyRequest(((WebTask) task).getUrl(), user));
}
if(task instanceof TextTask) {
taskText = ((TextTask) task).getText();
}
//todo!!!!!
return Response.ok().entity(taskText).build();
}
//todo: remove proxy resource
private InputStream proxyRequest(String url, User user) throws IOException
{
CloseableHttpClient httpClient = HttpClients.createDefault();
HttpGet httpget = new HttpGet(url);
httpget.setHeader("CTF-User", user.getName());
CloseableHttpResponse execute = httpClient.execute(httpget);
HttpEntity entity = execute.getEntity();
return entity.getContent();
}
private String readStream(InputStream input) throws IOException
{
try (BufferedReader buffer = new BufferedReader(new InputStreamReader(input))) {
return buffer.lines().collect(Collectors.joining("\n"));
}
return Response.ok().entity(task.getTextForUser(user)).build();
}
}
......@@ -4,7 +4,6 @@ import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.beans.tasks.Task;
import com.telephoners.krakyournet.ctf.repositories.TasksRepository;
import io.dropwizard.auth.Auth;
import org.apache.commons.collections.MapUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
......@@ -15,7 +14,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
import java.util.Map;
@Singleton
@Path(value = "/tasks")
......@@ -41,8 +39,13 @@ public class TasksResource
public Response getUserFlags(@Auth User user, final @PathParam("username") String username)
{
if (user.isAdmin()) {
Map<List<String>, Integer> userFlagsHashes = tasksRepository.getUserFlagsHashes(username);
return Response.ok().entity(MapUtils.invertMap(userFlagsHashes)).build();
try {
return Response.ok()
.entity(tasksRepository.getUserFlagsHashes(username))
.build();
} catch (Exception e) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
}
return Response.status(Response.Status.UNAUTHORIZED).build();
}
......
......@@ -5,6 +5,7 @@ import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.repositories.TeamsRepository;
import io.dropwizard.auth.Auth;
import org.apache.commons.io.FileUtils;
import javax.inject.Inject;
import javax.inject.Singleton;
......@@ -14,7 +15,8 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.Optional;
import java.io.File;
import java.io.IOException;
@Singleton
@Path("/")
......@@ -32,28 +34,33 @@ public class UtilResource
@GET
@Path("/whoami")
public ImmutableMap<Object, Object> getUserData(@Auth User user)
public Response getUserData(@Auth User user)
{
ImmutableMap.Builder<Object, Object> responseBuilder = ImmutableMap.builder();
String userName = user.getName();
responseBuilder.put("userName", userName);
Optional<Team> team = teamsRepository.getTeamByUserName(userName);
if (team.isPresent()) {
responseBuilder.put("teamName", team.get().getName());
if (!user.isAdmin()) {
Team team = teamsRepository.getTeamByUserName(userName);
responseBuilder.put("teamName", team.getName());
}
return responseBuilder.build();
else {
responseBuilder.put("isAdmin", true);
}
return Response.ok()
.entity(responseBuilder.build())
.build();
}
//todo: cleanup
@GET
@Path("/auth")
public Response auth(@Auth User user)
public Response auth(@Auth User user) throws IOException
{
String jsRedirect = "<script type=\"text/javascript\">\n" +
"<!--\n" +
"window.location = \"http://\" + window.location.host + \"/page\"\n" +
"//-->\n" +
"</script>";
return Response.ok().entity(jsRedirect).header(HttpHeaders.CONTENT_TYPE, "text/html").header(HttpHeaders.WWW_AUTHENTICATE, "Basic").build();
String redirectHtml = new String(FileUtils.readFileToByteArray(new File("service/src/main/resources/assets/redirect.html")));
return Response.ok()
.entity(redirectHtml)
.header(HttpHeaders.CONTENT_TYPE, "text/html")
.header(HttpHeaders.WWW_AUTHENTICATE, "Basic")
.build();
}
}
......@@ -3,4 +3,4 @@
</div>
<div style="text-align:center;width:100%">made with <i class="fa fa-heart"></i> by <a
href="http://telephoners.agh.edu.pl" id="telephoners"><img
src="/page/statics/img/logo_telephoners.svg"></img></a></div>
\ No newline at end of file
src="/statics/img/logo_telephoners.svg"></img></a></div>
\ No newline at end of file
......@@ -6,7 +6,7 @@
<link href="data:image/x-icon;base64,AAABAAEAEBAAAAEAIABoBAAAFgAAACgAAAAQAAAAIAAAAAEAIAAAAAAAAAQAABILAAASCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwcHADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhoaABqamobkZGRv5mZmUYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACDg4MbiYmJwYWFhVySkpK+oqKiQpOTk0IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJOTk1eNjY3jl5eX/aOjo/CsrKxVAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqqoZnJyc96qqqr64uLggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALW1tQCoqKhGp6en27GxsXK1tbUCAAAAAAAAAAAAAAAAAAAAAIGBgQCAgIBAgICAPoCAgAKAgIAFAAAAALKysgimpqaPq6ury7e3tysAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACqqqpotbW1rqioqJShoaHXnp6ef6ioqK2np6fNr6+vg7W1tQYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtbW1i4GBgSKqqqo4rKysl6mpqV6cnJzYra2tnAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAKysrImkpKTYkZGRLAAAAACAgIATo6Ojk6qqqmoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACmpqaQpKSklwAAAACAgIAhsLCwaaWlpcyoqKg8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAr6+vrZycnOSTk5O5oqKi1oCAgAmnp6dsqampUwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMPDww3KyspQxsbGd5eXl3menp66vLy8nbW1tQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//8AAP/vAAD/hwAA/4EAAP/BAAD/wwAA/wcAAIIfAACAPwAAgP8AAIj/AACQ/wAAgP8AAID/AAD//wAA//8AAA==" rel="icon" type="image/x-icon">
<link rel="stylesheet" href="/webjars/materializecss/css/materialize.min.css">
<link rel="stylesheet" href="/webjars/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="/page/statics/css/main.min.css">
<link rel="stylesheet" href="/statics/css/main.min.css">
</head>
<nav ng-controller="NavigationController">
......@@ -15,9 +15,9 @@
<span class="title"></span>
</a>
<ul id="nav-mobile" class="right hide-on-med-and-down">
<li ng-if="logged"><a href="page#/home">Początek</a></li>
<li ng-if="logged"><a href="page#/tasks">Zadania</a></li>
<li ng-if="logged"><a href="page#/scores">Wyniki</a></li>
<li ng-if="logged"><a href="#/home">Początek</a></li>
<li ng-if="logged"><a href="#/tasks">Zadania</a></li>
<li ng-if="logged"><a href="#/scores">Wyniki</a></li>
<li ng-if="logged" ng-click="logout()"><a href="page#/">Wyloguj</a></li>
</ul>
</div>
......@@ -34,6 +34,6 @@
<script type="text/javascript" src="/webjars/angularjs/angular.min.js"></script>
<script type="text/javascript" src="/webjars/angular-ui-router/angular-ui-router.min.js"></script>
<script type="text/javascript" src="/webjars/angular-cookies/angular-cookies.min.js"></script>
<script type="text/javascript" src="/page/statics/js/app.min.js"></script>
<script type="text/javascript" src="/statics/js/app.min.js"></script>
</html>
<!DOCTYPE html>
<html lang="en" ng-app="ctfApp">
<head>
</head>
<script type="text/javascript">
window.location = "http://" + window.location.host;
</script>
</body>
</html>
body {
background: url('/page/statics/img/bkg.jpg');
background: url('/statics/img/bkg.jpg');
color: white;
}
......
......@@ -12,27 +12,27 @@
.state('home', {
url: '/home',
templateUrl: '/page/home.html'
templateUrl: '/home.html'
})
.state('login', {
url: '/login',
templateUrl: '/page/login.html'
templateUrl: '/login.html'
})
.state('tasks', {
url: '/tasks',
templateUrl: '/page/tasks.html'
templateUrl: '/tasks.html'
})
.state('task', {
url: '/task/:taskLevel',
templateUrl: '/page/task.html'
templateUrl: '/task.html'
})
.state('scores', {
url: '/scores',
templateUrl: '/page/scoresheet.html'
templateUrl: '/scoresheet.html'
});
}]);
......
......@@ -2,7 +2,7 @@
<div class="row">
<div class="col s12">
<a href="/page#/tasks" class="breadcrumb"><i class="fa fa-angle-left" style="margin-right:20px"></i> Powrót</a>
<a href="#/tasks" class="breadcrumb"><i class="fa fa-angle-left" style="margin-right:20px"></i> Powrót</a>
<h1 class="left-align">{{task.name}}</h1>
</div>
</div>
......
......@@ -11,7 +11,7 @@
<p>Poziom {{task.level}}. Super zadanie.</p>
</div>
<div class="card-action">
<a href="/page#/task/{{task.level}}">Przejdź do zadania</a></td>
<a href="#/task/{{task.level}}">Przejdź do zadania</a></td>
</div>
</div>
</div>
......
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