Commit ab44e677 authored by adam's avatar adam

Merge branch 'master' into dev/tests

parents b812fb1f d74fbd2d
...@@ -10,6 +10,7 @@ dbPort: 27017 ...@@ -10,6 +10,7 @@ dbPort: 27017
dbName: db dbName: db
flagHashMethod: "MD5" flagHashMethod: "MD5"
salt: "SECURE_SALT"
admins: admins:
- name: "gpietrus_admin" - name: "gpietrus_admin"
......
package com.telephoners.krakyournet.ctf; package com.telephoners.krakyournet.ctf;
import com.bazaarvoice.dropwizard.webjars.WebJarBundle; 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.Guice;
import com.google.inject.Injector; 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.PurgeDatabaseCommand;
import com.telephoners.krakyournet.ctf.commands.RegisterTasksCommand; import com.telephoners.krakyournet.ctf.commands.RegisterTasksCommand;
import com.telephoners.krakyournet.ctf.commands.RegisterTeamsCommand; import com.telephoners.krakyournet.ctf.commands.RegisterTeamsCommand;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration; import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.logging.LoggingFilter; import com.telephoners.krakyournet.ctf.logging.LoggingFilter;
import com.telephoners.krakyournet.ctf.modules.ApplicationModule; 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.repositories.UsersRepository;
import com.telephoners.krakyournet.ctf.resources.*;
import io.dropwizard.Application; import io.dropwizard.Application;
import io.dropwizard.assets.AssetsBundle; import io.dropwizard.assets.AssetsBundle;
import io.dropwizard.auth.AuthDynamicFeature; import io.dropwizard.auth.AuthDynamicFeature;
import io.dropwizard.auth.AuthValueFactoryProvider; import io.dropwizard.auth.AuthValueFactoryProvider;
import io.dropwizard.auth.Authorizer;
import io.dropwizard.auth.basic.BasicCredentialAuthFilter; import io.dropwizard.auth.basic.BasicCredentialAuthFilter;
import io.dropwizard.jersey.setup.JerseyEnvironment; import io.dropwizard.jersey.setup.JerseyEnvironment;
import io.dropwizard.setup.Bootstrap; import io.dropwizard.setup.Bootstrap;
import io.dropwizard.setup.Environment; import io.dropwizard.setup.Environment;
import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature; import org.glassfish.jersey.server.filter.RolesAllowedDynamicFeature;
import java.io.IOException;
import java.util.stream.Stream; import java.util.stream.Stream;
public class CTFApplication extends Application<ApplicationConfiguration> public class CTFApplication extends Application<ApplicationConfiguration>
...@@ -33,8 +34,7 @@ public class CTFApplication extends Application<ApplicationConfiguration> ...@@ -33,8 +34,7 @@ public class CTFApplication extends Application<ApplicationConfiguration>
@Override @Override
public void initialize(final Bootstrap<ApplicationConfiguration> bootstrap) public void initialize(final Bootstrap<ApplicationConfiguration> bootstrap)
{ {
//todo: refactor, will not work on artifact? bootstrap.addBundle(new AssetsBundle("/assets_build", "/", "index.html"));
bootstrap.addBundle(new AssetsBundle("/assets_build", "/page", "index.html"));
bootstrap.addCommand(new PurgeDatabaseCommand()); bootstrap.addCommand(new PurgeDatabaseCommand());
bootstrap.addCommand(new RegisterTasksCommand()); bootstrap.addCommand(new RegisterTasksCommand());
bootstrap.addCommand(new RegisterTeamsCommand()); bootstrap.addCommand(new RegisterTeamsCommand());
...@@ -43,26 +43,20 @@ public class CTFApplication extends Application<ApplicationConfiguration> ...@@ -43,26 +43,20 @@ public class CTFApplication extends Application<ApplicationConfiguration>
.forEach(bootstrap::addBundle); .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(); JerseyEnvironment jersey = environment.jersey();
jersey.register(injector.getInstance(TeamsResource.class)); final ClassPath classPath = ClassPath.from(this.getClass().getClassLoader());
jersey.register(injector.getInstance(TasksResource.class)); ImmutableSet<ClassPath.ClassInfo> resourceClasses = classPath.getTopLevelClasses("com.telephoners.krakyournet.ctf.resources");
jersey.register(injector.getInstance(ProxyResource.class)); resourceClasses.stream()
jersey.register(injector.getInstance(SolutionsResource.class)); .forEach(classInfo -> jersey.register(injector.getInstance(classInfo.load())));
jersey.register(injector.getInstance(TaskResource.class));
jersey.register(injector.getInstance(UtilResource.class));
} }
private void registerAuthFeatures(Environment environment) private void registerAuthFeatures(Environment environment)
{ {
environment.jersey().register(new AuthDynamicFeature( environment.jersey().register(new AuthDynamicFeature(
new BasicCredentialAuthFilter.Builder<User>() new BasicCredentialAuthFilter.Builder<User>()
.setAuthenticator(injector.getInstance(ExampleAuthenticator.class)) .setAuthenticator(injector.getInstance(UserAuthenticator.class))
.setAuthorizer(new ExampleAuthorizer())
.setRealm("SUPER SECRET STUFF")
//.setPrefix("Not-So-Basic")
.buildAuthFilter())); .buildAuthFilter()));
environment.jersey().register(RolesAllowedDynamicFeature.class); environment.jersey().register(RolesAllowedDynamicFeature.class);
environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class)); environment.jersey().register(new AuthValueFactoryProvider.Binder<>(User.class));
...@@ -89,16 +83,6 @@ public class CTFApplication extends Application<ApplicationConfiguration> ...@@ -89,16 +83,6 @@ public class CTFApplication extends Application<ApplicationConfiguration>
applicationConfiguration.getAdmins().forEach(usersRepository::add); 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) private Injector createInjector(ApplicationConfiguration applicationConfiguration)
{ {
return Guice.createInjector(new ApplicationModule(applicationConfiguration)); return Guice.createInjector(new ApplicationModule(applicationConfiguration));
......
...@@ -8,13 +8,12 @@ import io.dropwizard.auth.AuthenticationException; ...@@ -8,13 +8,12 @@ import io.dropwizard.auth.AuthenticationException;
import io.dropwizard.auth.Authenticator; import io.dropwizard.auth.Authenticator;
import io.dropwizard.auth.basic.BasicCredentials; import io.dropwizard.auth.basic.BasicCredentials;
public class ExampleAuthenticator implements Authenticator<BasicCredentials, User> public class UserAuthenticator implements Authenticator<BasicCredentials, User>
{ {
private UsersRepository usersRepository; private UsersRepository usersRepository;
@Inject @Inject
public ExampleAuthenticator(UsersRepository usersRepository) public UserAuthenticator(UsersRepository usersRepository)
{ {
this.usersRepository = usersRepository; this.usersRepository = usersRepository;
} }
......
...@@ -5,6 +5,8 @@ import org.bson.types.ObjectId; ...@@ -5,6 +5,8 @@ import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Id;
import static com.google.common.base.Preconditions.checkNotNull;
@Entity("solutions") @Entity("solutions")
public class Solution public class Solution
{ {
...@@ -17,10 +19,10 @@ public class Solution ...@@ -17,10 +19,10 @@ public class Solution
public Solution(Team team, Task task, Flag flag, String hashValue) public Solution(Team team, Task task, Flag flag, String hashValue)
{ {
this.team = team; this.team = checkNotNull(team);
this.task = task; this.task = checkNotNull(task);
this.flag = flag; this.flag = checkNotNull(flag);
this.hashValue = hashValue; this.hashValue = checkNotNull(hashValue);
} }
public Solution() public Solution()
......
package com.telephoners.krakyournet.ctf.beans.tasks; package com.telephoners.krakyournet.ctf.beans.tasks;
import com.fasterxml.jackson.annotation.JsonInclude; 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.helpers.PublicProperty;
import com.telephoners.krakyournet.ctf.beans.Flag; import com.telephoners.krakyournet.ctf.beans.Flag;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Entity;
import org.mongodb.morphia.annotations.Id; import org.mongodb.morphia.annotations.Id;
import java.io.IOException;
import java.util.List; import java.util.List;
@Entity("tasks") @Entity("tasks")
...@@ -32,6 +34,8 @@ public abstract class Task ...@@ -32,6 +34,8 @@ public abstract class Task
{ {
} }
public abstract String getTextForUser(User user) throws IOException;
public String getName() public String getName()
{ {
return name; return name;
......
...@@ -2,6 +2,7 @@ package com.telephoners.krakyournet.ctf.beans.tasks; ...@@ -2,6 +2,7 @@ package com.telephoners.krakyournet.ctf.beans.tasks;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.telephoners.krakyournet.ctf.beans.Flag; import com.telephoners.krakyournet.ctf.beans.Flag;
import com.telephoners.krakyournet.ctf.beans.User;
import org.mongodb.morphia.annotations.Entity; import org.mongodb.morphia.annotations.Entity;
import java.util.List; import java.util.List;
...@@ -22,7 +23,7 @@ public class TextTask extends Task ...@@ -22,7 +23,7 @@ public class TextTask extends Task
{ {
} }
public String getText() public String getTextForUser(User user)
{ {
return text; return text;
} }
......
...@@ -2,8 +2,17 @@ package com.telephoners.krakyournet.ctf.beans.tasks; ...@@ -2,8 +2,17 @@ package com.telephoners.krakyournet.ctf.beans.tasks;
import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonInclude;
import com.telephoners.krakyournet.ctf.beans.Flag; 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 org.mongodb.morphia.annotations.Entity;
import java.io.IOException;
import java.io.InputStream;
import java.util.List; import java.util.List;
@Entity("tasks") @Entity("tasks")
...@@ -22,6 +31,11 @@ public class WebTask extends Task ...@@ -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() public String getUrl()
{ {
return url; return url;
...@@ -31,4 +45,14 @@ public class WebTask extends Task ...@@ -31,4 +45,14 @@ public class WebTask extends Task
{ {
this.url = url; 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 ...@@ -18,6 +18,7 @@ public class ApplicationConfiguration extends Configuration
private List<TextTask> textTasks; private List<TextTask> textTasks;
private List<WebTask> webTasks; private List<WebTask> webTasks;
private List<User> admins; private List<User> admins;
private String salt;
public List<User> getAdmins() public List<User> getAdmins()
{ {
...@@ -98,4 +99,14 @@ public class ApplicationConfiguration extends Configuration ...@@ -98,4 +99,14 @@ public class ApplicationConfiguration extends Configuration
{ {
this.webTasks = webTasks; 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; package com.telephoners.krakyournet.ctf.modules;
import com.google.inject.AbstractModule; import com.google.inject.AbstractModule;
import com.mongodb.MongoClient;
import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration; import com.telephoners.krakyournet.ctf.core.ApplicationConfiguration;
import com.telephoners.krakyournet.ctf.providers.DatastoreProvider;
import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Datastore;
import org.mongodb.morphia.Morphia;
public class ApplicationModule extends AbstractModule public class ApplicationModule extends AbstractModule
{ {
private ApplicationConfiguration applicationConfiguration; private ApplicationConfiguration applicationConfiguration;
public ApplicationModule(ApplicationConfiguration applicationConfiguration) public ApplicationModule(ApplicationConfiguration applicationConfiguration)
...@@ -19,14 +17,7 @@ public class ApplicationModule extends AbstractModule ...@@ -19,14 +17,7 @@ public class ApplicationModule extends AbstractModule
@Override @Override
protected void configure() protected void configure()
{ {
bind(ApplicationConfiguration.class).toInstance(applicationConfiguration); //todo: refactor bind(ApplicationConfiguration.class).toInstance(applicationConfiguration);
bind(Datastore.class).toInstance(new DatastoreProvider(applicationConfiguration).getDatastore());
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);
} }
} }
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; package com.telephoners.krakyournet.ctf.repositories;
import org.mongodb.morphia.Datastore;
import javax.inject.Inject;
import java.lang.reflect.ParameterizedType;
import java.util.List; import java.util.List;
/** public abstract class Repository<T>
* Created by gpietrus on 20.02.2016. {
*/ protected Datastore datastore;
public interface Repository {
// void getByTaskName(UUID uuid); @Inject
List getAll(); 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; 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.Solution;
import com.telephoners.krakyournet.ctf.beans.Team; import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.tasks.Task; import com.telephoners.krakyournet.ctf.beans.tasks.Task;
...@@ -10,47 +9,28 @@ import javax.inject.Inject; ...@@ -10,47 +9,28 @@ import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Singleton @Singleton
public class SolutionsRepository implements Repository public class SolutionsRepository extends Repository<Solution>
{ {
private Datastore datastore; private TasksRepository tasksRepository;
@Inject @Inject
public SolutionsRepository(Datastore datastore) public SolutionsRepository(TasksRepository tasksRepository,
Datastore datastore)
{ {
this.datastore = datastore; super(datastore);
} this.tasksRepository = tasksRepository;
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
} }
public Map<Integer, List<String>> getTeamSolutions(Team team) 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>() .collect(Collectors.groupingBy(new Function<Solution, Task>()
{ {
@Override @Override
...@@ -58,7 +38,9 @@ public class SolutionsRepository implements Repository ...@@ -58,7 +38,9 @@ public class SolutionsRepository implements Repository
{ {
return solution1.getTask(); return solution1.getTask();
} }
})).entrySet().stream() }))
.entrySet()
.stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
taskSolutions -> taskSolutions.getKey().getLevel(), taskSolutions -> taskSolutions.getKey().getLevel(),
taskSolutions -> taskSolutions.getValue().stream() taskSolutions -> taskSolutions.getValue().stream()
...@@ -66,22 +48,31 @@ public class SolutionsRepository implements Repository ...@@ -66,22 +48,31 @@ public class SolutionsRepository implements Repository
)); ));
} }
//todo: use datastore filter public boolean isAlreadySubmittedSolution(Solution solution)
public boolean exists(Solution solution)
{ {
//todo: refactor, ugly ;( return datastore.find(Solution.class)
String value = solution.getFlag().getValue(); .filter("flag.value", solution.getFlag().getValue())
Optional<Solution> matchedSolution = getAll().stream() .get() != null;
.filter(new Predicate<Solution>() }
{
@Override public List<Integer> getCompletedTasks(Team team)
public boolean test(Solution solution) {
{ Map<Integer, List<String>> teamSolutions = getTeamSolutions(team);
//todo: compare flags, not flag values return tasksRepository.getAll().stream()
return solution.getFlag().getValue().equals(value); .filter(task -> {
} List<String> teamTaskSolutions = teamSolutions.get(task.getLevel());
return teamTaskSolutions != null && teamTaskSolutions.size() == task.getFlags().size();
}) })
.findFirst(); .map(Task::getLevel)
return matchedSolution.isPresent(); .collect(Collectors.toList());
}
public boolean submitSolution(Solution solution)
{
if (!isAlreadySubmittedSolution(solution)) {
add(solution);
return true;
}
return false;
} }
} }
\ No newline at end of file
package com.telephoners.krakyournet.ctf.repositories; package com.telephoners.krakyournet.ctf.repositories;
import com.telephoners.krakyournet.ctf.beans.Flag; 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.User;
import com.telephoners.krakyournet.ctf.beans.tasks.Task; 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 com.telephoners.krakyournet.ctf.helpers.DBObjectUtils;
import javafx.util.Pair; import javafx.util.Pair;
import org.apache.commons.codec.binary.Hex;
import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Datastore;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Singleton @Singleton
public class TasksRepository implements Repository public class TasksRepository extends Repository<Task>
{ {
private ApplicationConfiguration applicationConfiguration;
private Datastore datastore; private Datastore datastore;
private TeamsRepository teamsRepository; private UsersRepository usersRepository;
private SolutionsRepository solutionsRepository; private HashValidator hashValidator;
private String salt = "SECURE_SALT"; //todo: move to configuration!
@Inject @Inject
public TasksRepository(ApplicationConfiguration applicationConfiguration, Datastore datastore, public TasksRepository(Datastore datastore,
TeamsRepository teamsRepository, SolutionsRepository solutionsRepository) UsersRepository usersRepository,
HashValidator hashValidator)
{ {
this.applicationConfiguration = applicationConfiguration; super(datastore);
this.datastore = datastore; this.datastore = datastore;
this.teamsRepository = teamsRepository; this.usersRepository = usersRepository;
this.solutionsRepository = solutionsRepository; this.hashValidator = hashValidator;
} }
public Task getByLevel(int level) public Task getByLevel(int level)
...@@ -49,16 +39,6 @@ public class TasksRepository implements Repository ...@@ -49,16 +39,6 @@ public class TasksRepository implements Repository
.get(); .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() public List<Task> getAllPublic()
{ {
return datastore.createQuery(Task.class) return datastore.createQuery(Task.class)
...@@ -66,135 +46,23 @@ public class TasksRepository implements Repository ...@@ -66,135 +46,23 @@ public class TasksRepository implements Repository
.asList(); .asList();
} }
public List<Task> getAll() public Map<Integer, List<String>> getUserFlagsHashes(String username)
{ {
return datastore.createQuery(Task.class).asList(); return getAll().stream()
}
public void add(Task task)
{
datastore.save(task);
}
//todo: refactor?
public Map<List<String>, Integer> getUserFlagsHashes(String username)
{
return this.getAll().stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
task -> { Task::getLevel,
List<String> collect = task.getFlags().stream() task -> task.getFlags().stream()
.map(flag -> calculateHashValue(username, flag.getValue())) .map(flag -> hashValidator.calculateHashValue(usersRepository.getUserByName(username), flag.getValue()))
.collect(Collectors.toList()); .collect(Collectors.toList())
return collect;
},
Task::getLevel
)); ));
} }
//todo: refactor with the function below public Pair<Task, Flag> getTaskFlagPairByHashValue(User user, String userHash, int taskLevel)
private Optional<Pair<Task, Flag>> getTaskFlagPairByHashValue(User user, String userHash)
{ {
String username = user.getName(); Flag matchedFlag = getByLevel(taskLevel).getFlags().stream()
//todo: collapse lambdas .filter(flag -> hashValidator.calculateHashValue(user, flag.getValue()).equals(userHash))
Optional<Pair<Task, Flag>> matched = this.getAll().stream() .findFirst()
.collect(Collectors.toMap( .get();
task -> task, return new Pair<>(getByLevel(taskLevel), matchedFlag);
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)
{
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());
} }
} }
\ No newline at end of file
package com.telephoners.krakyournet.ctf.repositories; 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.Team;
import com.telephoners.krakyournet.ctf.beans.User; import com.telephoners.krakyournet.ctf.beans.User;
import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Datastore;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Created by gpietrus on 20.02.2016.
*/
@Singleton @Singleton
public class TeamsRepository implements Repository public class TeamsRepository extends Repository<Team>
{ {
private Datastore datastore; private Datastore datastore;
private ApplicationConfiguration applicationConfiguration;
private UsersRepository usersRepository; private UsersRepository usersRepository;
@Inject @Inject
public TeamsRepository(Datastore datastore, ApplicationConfiguration applicationConfiguration, public TeamsRepository(Datastore datastore,
UsersRepository usersRepository) UsersRepository usersRepository)
{ {
super(datastore);
this.datastore = datastore; this.datastore = datastore;
this.applicationConfiguration = applicationConfiguration;
this.usersRepository = usersRepository; this.usersRepository = usersRepository;
} }
public Optional<Team> getTeamByUser(User user) public Team getTeamByUser(User user)
{ {
return datastore.createQuery(Team.class).asList().stream() return datastore.createQuery(Team.class).asList().stream()
.filter(team -> team.getMembers().contains(user)) .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)); 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; ...@@ -3,34 +3,21 @@ package com.telephoners.krakyournet.ctf.repositories;
import com.google.inject.Inject; import com.google.inject.Inject;
import com.google.inject.Singleton; import com.google.inject.Singleton;
import com.telephoners.krakyournet.ctf.beans.User; import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.providers.MessageDigestProvider;
import io.dropwizard.auth.basic.BasicCredentials; import io.dropwizard.auth.basic.BasicCredentials;
import org.apache.commons.codec.binary.Hex; import org.apache.commons.codec.binary.Hex;
import org.mongodb.morphia.Datastore; import org.mongodb.morphia.Datastore;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
@Singleton @Singleton
public class UsersRepository implements Repository public class UsersRepository extends Repository<User>
{ {
private Datastore datastore; private MessageDigestProvider messageDigestProvider;
private MessageDigest messageDigest;
@Inject @Inject
public UsersRepository(Datastore datastore) public UsersRepository(Datastore datastore, MessageDigestProvider messageDigestProvider)
{
this.datastore = datastore;
try {
messageDigest = MessageDigest.getInstance("MD5"); //todo
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public void add(User user)
{ {
datastore.save(user); super(datastore);
this.messageDigestProvider = messageDigestProvider;
} }
public User getUserByName(String username) public User getUserByName(String username)
...@@ -44,13 +31,7 @@ public class UsersRepository implements Repository ...@@ -44,13 +31,7 @@ public class UsersRepository implements Repository
{ {
return datastore.createQuery(User.class) return datastore.createQuery(User.class)
.field("name").equal(basicCredentials.getUsername()) .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(); .get();
} }
@Override
public List getAll()
{
return null;
}
} }
package com.telephoners.krakyournet.ctf.resources; 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.Team;
import com.telephoners.krakyournet.ctf.beans.User; 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.SolutionsRepository;
import com.telephoners.krakyournet.ctf.repositories.TasksRepository; import com.telephoners.krakyournet.ctf.repositories.TasksRepository;
import com.telephoners.krakyournet.ctf.repositories.TeamsRepository; import com.telephoners.krakyournet.ctf.repositories.TeamsRepository;
import io.dropwizard.auth.Auth; import io.dropwizard.auth.Auth;
import javafx.util.Pair;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import javax.ws.rs.GET; import javax.ws.rs.*;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@Singleton @Singleton
...@@ -34,16 +35,21 @@ public class SolutionsResource ...@@ -34,16 +35,21 @@ public class SolutionsResource
TeamsRepository teamsRepository) TeamsRepository teamsRepository)
{ {
this.solutionsRepository = solutionsRepository; this.solutionsRepository = solutionsRepository;
this.tasksRepository = tasksRepository;
this.teamsRepository = teamsRepository; this.teamsRepository = teamsRepository;
this.tasksRepository = tasksRepository;
} }
@POST @POST
@Path("/{task_level}")
public Response submitSolution(@Auth User user, public Response submitSolution(@Auth User user,
@PathParam("task_level") int taskLevel,
String hash) throws Exception String hash) throws Exception
{ {
if (tasksRepository.checkHash(user, hash)) { Pair<Task, Flag> taskFlagPair = tasksRepository.getTaskFlagPairByHashValue(user, hash, taskLevel);
return Response.ok().build(); 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(); return Response.status(Response.Status.NOT_ACCEPTABLE).build();
} }
...@@ -52,8 +58,7 @@ public class SolutionsResource ...@@ -52,8 +58,7 @@ public class SolutionsResource
@Path("/completed") @Path("/completed")
public List<Integer> getTeamCompletedTasks(@Auth User user) public List<Integer> getTeamCompletedTasks(@Auth User user)
{ {
Optional<Team> team = teamsRepository.getTeamByUser(user); return solutionsRepository.getCompletedTasks(teamsRepository.getTeamByUser(user));
return tasksRepository.getCompletedTasks(team.get());
} }
@GET @GET
...@@ -64,21 +69,20 @@ public class SolutionsResource ...@@ -64,21 +69,20 @@ public class SolutionsResource
.stream() .stream()
.collect(Collectors.toMap( .collect(Collectors.toMap(
Team::getName, Team::getName,
team -> tasksRepository.getCompletedTasks(team) team -> solutionsRepository.getCompletedTasks(team)
)); ));
} }
@GET @GET
@Path("/my") @Path("/my")
public Map<Integer, List<String>> getTeamSolutions(@Auth User user) public Map<Integer, List<String>> getTeamSolutions(@Auth User user)
{ {
Optional<Team> team = teamsRepository.getTeamByUser(user); Team team = teamsRepository.getTeamByUser(user);
return solutionsRepository.getTeamSolutions(team.get()); return solutionsRepository.getTeamSolutions(team);
} }
@GET @GET
@Path("/all") @Path("/all")
//todo: should return completed, not all flags?
public Map<String, Map<Integer, List<String>>> getTeamsSolutions() public Map<String, Map<Integer, List<String>>> getTeamsSolutions()
{ {
return teamsRepository.getAll() return teamsRepository.getAll()
......
...@@ -46,36 +46,6 @@ public class TaskResource ...@@ -46,36 +46,6 @@ public class TaskResource
if (task == null) { if (task == null) {
return Response.status(Response.Status.BAD_REQUEST).build(); return Response.status(Response.Status.BAD_REQUEST).build();
} }
return Response.ok().entity(task.getTextForUser(user)).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"));
}
} }
} }
...@@ -4,7 +4,6 @@ import com.telephoners.krakyournet.ctf.beans.User; ...@@ -4,7 +4,6 @@ import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.beans.tasks.Task; import com.telephoners.krakyournet.ctf.beans.tasks.Task;
import com.telephoners.krakyournet.ctf.repositories.TasksRepository; import com.telephoners.krakyournet.ctf.repositories.TasksRepository;
import io.dropwizard.auth.Auth; import io.dropwizard.auth.Auth;
import org.apache.commons.collections.MapUtils;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
...@@ -15,7 +14,6 @@ import javax.ws.rs.Produces; ...@@ -15,7 +14,6 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.List; import java.util.List;
import java.util.Map;
@Singleton @Singleton
@Path(value = "/tasks") @Path(value = "/tasks")
...@@ -41,8 +39,13 @@ public class TasksResource ...@@ -41,8 +39,13 @@ public class TasksResource
public Response getUserFlags(@Auth User user, final @PathParam("username") String username) public Response getUserFlags(@Auth User user, final @PathParam("username") String username)
{ {
if (user.isAdmin()) { if (user.isAdmin()) {
Map<List<String>, Integer> userFlagsHashes = tasksRepository.getUserFlagsHashes(username); try {
return Response.ok().entity(MapUtils.invertMap(userFlagsHashes)).build(); 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(); return Response.status(Response.Status.UNAUTHORIZED).build();
} }
......
...@@ -5,6 +5,7 @@ import com.telephoners.krakyournet.ctf.beans.Team; ...@@ -5,6 +5,7 @@ import com.telephoners.krakyournet.ctf.beans.Team;
import com.telephoners.krakyournet.ctf.beans.User; import com.telephoners.krakyournet.ctf.beans.User;
import com.telephoners.krakyournet.ctf.repositories.TeamsRepository; import com.telephoners.krakyournet.ctf.repositories.TeamsRepository;
import io.dropwizard.auth.Auth; import io.dropwizard.auth.Auth;
import org.apache.commons.io.FileUtils;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
...@@ -14,7 +15,8 @@ import javax.ws.rs.Produces; ...@@ -14,7 +15,8 @@ import javax.ws.rs.Produces;
import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.Optional; import java.io.File;
import java.io.IOException;
@Singleton @Singleton
@Path("/") @Path("/")
...@@ -32,28 +34,33 @@ public class UtilResource ...@@ -32,28 +34,33 @@ public class UtilResource
@GET @GET
@Path("/whoami") @Path("/whoami")
public ImmutableMap<Object, Object> getUserData(@Auth User user) public Response getUserData(@Auth User user)
{ {
ImmutableMap.Builder<Object, Object> responseBuilder = ImmutableMap.builder(); ImmutableMap.Builder<Object, Object> responseBuilder = ImmutableMap.builder();
String userName = user.getName(); String userName = user.getName();
responseBuilder.put("userName", userName); responseBuilder.put("userName", userName);
Optional<Team> team = teamsRepository.getTeamByUserName(userName); if (!user.isAdmin()) {
if (team.isPresent()) { Team team = teamsRepository.getTeamByUserName(userName);
responseBuilder.put("teamName", team.get().getName()); responseBuilder.put("teamName", team.getName());
} }
return responseBuilder.build(); else {
responseBuilder.put("isAdmin", true);
}
return Response.ok()
.entity(responseBuilder.build())
.build();
} }
//todo: cleanup
@GET @GET
@Path("/auth") @Path("/auth")
public Response auth(@Auth User user) public Response auth(@Auth User user) throws IOException
{ {
String jsRedirect = "<script type=\"text/javascript\">\n" + String redirectHtml = new String(FileUtils.readFileToByteArray(new File("service/src/main/resources/assets/redirect.html")));
"<!--\n" + return Response.ok()
"window.location = \"http://\" + window.location.host + \"/page\"\n" + .entity(redirectHtml)
"//-->\n" + .header(HttpHeaders.CONTENT_TYPE, "text/html")
"</script>"; .header(HttpHeaders.WWW_AUTHENTICATE, "Basic")
return Response.ok().entity(jsRedirect).header(HttpHeaders.CONTENT_TYPE, "text/html").header(HttpHeaders.WWW_AUTHENTICATE, "Basic").build(); .build();
} }
} }
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
</div> </div>
<div style="text-align:center;width:100%">made with <i class="fa fa-heart"></i> by <a <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 href="http://telephoners.agh.edu.pl" id="telephoners"><img
src="/page/statics/img/logo_telephoners.svg"></img></a></div> src="/statics/img/logo_telephoners.svg"></img></a></div>
\ No newline at end of file \ No newline at end of file
...@@ -6,7 +6,7 @@ ...@@ -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 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/materializecss/css/materialize.min.css">
<link rel="stylesheet" href="/webjars/font-awesome/css/font-awesome.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> </head>
<nav ng-controller="NavigationController"> <nav ng-controller="NavigationController">
...@@ -15,9 +15,9 @@ ...@@ -15,9 +15,9 @@
<span class="title"></span> <span class="title"></span>
</a> </a>
<ul id="nav-mobile" class="right hide-on-med-and-down"> <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="#/home">Początek</a></li>
<li ng-if="logged"><a href="page#/tasks">Zadania</a></li> <li ng-if="logged"><a href="#/tasks">Zadania</a></li>
<li ng-if="logged"><a href="page#/scores">Wyniki</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> <li ng-if="logged" ng-click="logout()"><a href="page#/">Wyloguj</a></li>
</ul> </ul>
</div> </div>
...@@ -34,6 +34,6 @@ ...@@ -34,6 +34,6 @@
<script type="text/javascript" src="/webjars/angularjs/angular.min.js"></script> <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-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="/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> </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 { body {
background: url('/page/statics/img/bkg.jpg'); background: url('/statics/img/bkg.jpg');
color: white; color: white;
} }
......
...@@ -12,27 +12,27 @@ ...@@ -12,27 +12,27 @@
.state('home', { .state('home', {
url: '/home', url: '/home',
templateUrl: '/page/home.html' templateUrl: '/home.html'
}) })
.state('login', { .state('login', {
url: '/login', url: '/login',
templateUrl: '/page/login.html' templateUrl: '/login.html'
}) })
.state('tasks', { .state('tasks', {
url: '/tasks', url: '/tasks',
templateUrl: '/page/tasks.html' templateUrl: '/tasks.html'
}) })
.state('task', { .state('task', {
url: '/task/:taskLevel', url: '/task/:taskLevel',
templateUrl: '/page/task.html' templateUrl: '/task.html'
}) })
.state('scores', { .state('scores', {
url: '/scores', url: '/scores',
templateUrl: '/page/scoresheet.html' templateUrl: '/scoresheet.html'
}); });
}]); }]);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div class="row"> <div class="row">
<div class="col s12"> <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> <h1 class="left-align">{{task.name}}</h1>
</div> </div>
</div> </div>
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
<p>Poziom {{task.level}}. Super zadanie.</p> <p>Poziom {{task.level}}. Super zadanie.</p>
</div> </div>
<div class="card-action"> <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> </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