@@ -19,37 +19,38 @@ sourceCompatibility = 1.8 | |||
targetCompatibility = 1.8 | |||
repositories { | |||
maven { | |||
url 'http://wytrem.github.io/maven' | |||
} | |||
maven { | |||
url 'https://paladin-framework.github.io/maven' | |||
} | |||
mavenCentral() | |||
maven { | |||
url 'http://wytrem.github.io/maven' | |||
} | |||
maven { | |||
url 'https://paladin-framework.github.io/maven' | |||
} | |||
mavenCentral() | |||
} | |||
dependencies { | |||
implementation( | |||
'com.sparkjava:spark-core:2.8.0', | |||
'net.sf.trove4j:trove4j:3.0.3', | |||
'com.google.code.gson:gson:2.8.5', | |||
'com.mashape.unirest:unirest-java:1.4.9', | |||
'org.apache.commons:commons-lang3:3.8.1', | |||
'org.apache.logging.log4j:log4j-core:2.10.0' | |||
) | |||
implementation('fr.litarvan.paladin:paladin-framework:1.1.0') { | |||
exclude group: 'org.apache.httpcomponents', module: 'httpcore-nio' | |||
exclude group: 'org.apache.logging.log4j', module: 'log4j-core' | |||
} | |||
implementation( | |||
'com.sparkjava:spark-core:2.8.0', | |||
'net.sf.trove4j:trove4j:3.0.3', | |||
'com.google.code.gson:gson:2.8.5', | |||
'com.mashape.unirest:unirest-java:1.4.9', | |||
'org.apache.commons:commons-lang3:3.8.1', | |||
'org.apache.logging.log4j:log4j-core:2.10.0', | |||
'com.auth0:java-jwt:3.10.2' | |||
) | |||
implementation('fr.litarvan.paladin:paladin-framework:1.1.0') { | |||
exclude group: 'org.apache.httpcomponents', module: 'httpcore-nio' | |||
exclude group: 'org.apache.logging.log4j', module: 'log4j-core' | |||
} | |||
} | |||
task fatJar(type: Jar) { | |||
from { | |||
configurations | |||
.runtimeClasspath | |||
.runtimeClasspath | |||
.findAll { !it.name.endsWith('pom') } | |||
.collect { it.isDirectory() ? it : zipTree(it) } | |||
} | |||
@@ -58,6 +59,6 @@ task fatJar(type: Jar) { | |||
baseName = 'dero-benchmarks' | |||
manifest { | |||
attributes 'Main-Class': mainClassName | |||
attributes 'Main-Class': mainClassName | |||
} | |||
} |
@@ -29,6 +29,7 @@ export default { | |||
} | |||
else { | |||
localStorage.removeItem("token") | |||
this.$router.push('/') | |||
} | |||
}) | |||
} | |||
@@ -7,7 +7,7 @@ import 'material-design-icons-iconfont/dist/material-design-icons.css' | |||
Vue.config.productionTip = false | |||
Vue.prototype.$api = "http://localhost:8081" | |||
Vue.prototype.$api = "http://localhost:8080" | |||
Vue.use(Vuetify) | |||
new Vue({ | |||
@@ -25,7 +25,8 @@ export default { | |||
}, | |||
mounted() { | |||
let token = localStorage.getItem("token") | |||
if (token != null && token.length == 86) | |||
if (token != null) | |||
{ | |||
this.$router.push("/unconfirmedBenchmarks") | |||
} | |||
@@ -40,6 +41,7 @@ export default { | |||
password: this.password | |||
}) | |||
}).then(result => result.json()).then(json => { | |||
console.log(json) | |||
let valid = json.token != null | |||
this.alertType = valid ? "success" : "error" | |||
this.alertMessage = valid ? "You are now logged in!" : json.message | |||
@@ -62,16 +62,17 @@ export default { | |||
}, | |||
mounted() { | |||
let token = localStorage.getItem("token") | |||
if (token == null || token.length != 86) //TODO must wait App.mounted() | |||
if (token == null) //TODO must wait App.mounted() | |||
{ | |||
this.$router.push("/login") | |||
return | |||
} | |||
} | |||
let headers = new Headers(); | |||
headers.append("Authorization", "Bearer " + token) | |||
fetch(this.$api + "/api/unconfirmedBenchmarks", { headers: headers }).then(result => result.json()).then(json => { | |||
console.log(json) | |||
this.benchmarks = json | |||
this.loading = false | |||
}) | |||
@@ -11,6 +11,7 @@ import fr.litarvan.paladin.Paladin; | |||
import fr.litarvan.paladin.PaladinBuilder; | |||
import fr.litarvan.paladin.PaladinConfig; | |||
import fr.slixe.benchmarks.http.SparkHttpServer; | |||
import fr.slixe.benchmarks.service.SessionManager; | |||
public class Main | |||
{ | |||
@@ -21,11 +22,20 @@ public class Main | |||
Paladin paladin = PaladinBuilder.create(App.class) | |||
.addModule(new MyModule()) | |||
.loadCommandLineArguments(args) | |||
.setSessionManager(new SessionManager()) | |||
.build(); | |||
PaladinConfig config = paladin.getConfig(); | |||
SparkHttpServer httpServer = new SparkHttpServer(paladin, config.get("port", int.class)); | |||
SparkHttpServer httpServer = new SparkHttpServer(paladin, config.get("port", int.class)); | |||
String sessionSecret = config.get("secret"); | |||
if (sessionSecret.isEmpty() || sessionSecret.length() < 6) { | |||
log.error("The secret key for Session Manager is empty or low!! Exiting..."); | |||
return; | |||
} | |||
((SessionManager) paladin.getSessionManager()).init(paladin, sessionSecret); | |||
if (config.get("enableSSL", boolean.class)) | |||
{ | |||
log.info("Enabling SSL..."); | |||
@@ -3,7 +3,9 @@ package fr.slixe.benchmarks; | |||
public class User { | |||
private final String username; | |||
@JsonIgnore | |||
private final String hashedPassword; | |||
@JsonIgnore | |||
private final String salt; | |||
public User(String username, String hashedPassword, String salt) | |||
@@ -1,7 +1,11 @@ | |||
package fr.slixe.benchmarks.http; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import fr.litarvan.paladin.AfterEvent; | |||
import fr.litarvan.paladin.BeforeEvent; | |||
import fr.litarvan.paladin.Session; | |||
import fr.litarvan.paladin.http.Middleware; | |||
import fr.litarvan.paladin.http.Request; | |||
import fr.litarvan.paladin.http.Response; | |||
@@ -11,12 +15,22 @@ import fr.slixe.benchmarks.User; | |||
public class AuthMiddleware extends Middleware | |||
{ | |||
private static final Logger log = LoggerFactory.getLogger("Auth Middleware"); | |||
@Override | |||
public void before(BeforeEvent event, Request request, Response response, Route route) throws RequestException | |||
{ | |||
if (request.getSession().get(User.class) == null) { | |||
log.info("{}", request.getMethod()); | |||
log.info(request.getIp()); | |||
log.info(request.getUri()); | |||
Session session = request.getSession(); | |||
if (session == null || session.get(User.class) == null) { | |||
log.info("NOT LOGGED IN"); | |||
throw new UnauthorizedOperationException(); | |||
} | |||
else { | |||
log.info("{}", session.get(User.class)); | |||
} | |||
} | |||
@Override | |||
@@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory | |||
import com.google.inject.Inject | |||
import fr.litarvan.paladin.Paladin | |||
import fr.litarvan.paladin.Session | |||
import fr.litarvan.paladin.http.Controller | |||
import fr.litarvan.paladin.http.routing.JsonBody | |||
@@ -12,6 +13,7 @@ import fr.litarvan.paladin.http.routing.RequestParams | |||
import fr.slixe.benchmarks.User | |||
import fr.slixe.benchmarks.http.InvalidParameterException | |||
import fr.slixe.benchmarks.service.AuthService | |||
import fr.slixe.benchmarks.service.SessionManager | |||
public class AuthController extends Controller { | |||
@@ -20,9 +22,12 @@ public class AuthController extends Controller { | |||
@Inject | |||
private AuthService authService | |||
@Inject | |||
private Paladin paladin; | |||
@JsonBody | |||
@RequestParams(required = ["username", "password"]) | |||
def login(String username, String password, Session session) | |||
def login(String username, String password) | |||
{ | |||
if (password.length() > 64) { | |||
throw new InvalidParameterException("Password is too long") | |||
@@ -36,8 +41,7 @@ public class AuthController extends Controller { | |||
log.info(String.format("User %s is now logged in.", user.getUsername())) | |||
session[User] = user | |||
Session session = ((SessionManager) paladin.getSessionManager()).createSession(user) | |||
[ | |||
token: session.token | |||
] | |||
@@ -0,0 +1,136 @@ | |||
package fr.slixe.benchmarks.service; | |||
import java.time.LocalDateTime; | |||
import java.time.ZoneOffset; | |||
import java.util.Date; | |||
import java.util.concurrent.TimeUnit; | |||
import com.auth0.jwt.JWT; | |||
import com.auth0.jwt.algorithms.Algorithm; | |||
import com.auth0.jwt.exceptions.SignatureVerificationException; | |||
import com.auth0.jwt.exceptions.TokenExpiredException; | |||
import com.auth0.jwt.interfaces.DecodedJWT; | |||
import com.auth0.jwt.interfaces.JWTVerifier; | |||
import com.google.gson.ExclusionStrategy; | |||
import com.google.gson.FieldAttributes; | |||
import com.google.gson.Gson; | |||
import com.google.gson.GsonBuilder; | |||
import fr.litarvan.paladin.ISessionManager; | |||
import fr.litarvan.paladin.Paladin; | |||
import fr.litarvan.paladin.Session; | |||
import fr.litarvan.paladin.http.Header; | |||
import fr.litarvan.paladin.http.Request; | |||
import fr.litarvan.paladin.http.Response; | |||
import fr.slixe.benchmarks.JsonIgnore; | |||
import fr.slixe.benchmarks.User; | |||
public class SessionManager implements ISessionManager { | |||
private final Gson gson = new GsonBuilder().addSerializationExclusionStrategy(new ExclusionStrategy() { | |||
@Override | |||
public boolean shouldSkipField(FieldAttributes f) { | |||
return f.getAnnotation(JsonIgnore.class) != null; | |||
} | |||
@Override | |||
public boolean shouldSkipClass(Class<?> clazz) { | |||
return false; | |||
} | |||
}).create(); | |||
private Paladin paladin; | |||
private Algorithm algorithm; | |||
private long expirationDelay; | |||
public SessionManager() | |||
{ | |||
this.expirationDelay = TimeUnit.DAYS.toSeconds(30); | |||
} | |||
public void init(Paladin paladin, String secret) | |||
{ | |||
this.paladin = paladin; | |||
this.algorithm = Algorithm.HMAC256(secret); | |||
} | |||
@Override | |||
public Session get(Request request, Response response) | |||
{ | |||
String token = request.getHeaderValue(Header.AUTHORIZATION); | |||
if (token != null) | |||
{ | |||
if (token.startsWith("Bearer") && token.length() > 7) | |||
{ | |||
token = token.substring(7); | |||
} | |||
try { | |||
JWTVerifier verifier = JWT.require(this.algorithm).build(); | |||
DecodedJWT decodedToken = verifier.verify(token); | |||
token = decodedToken.getToken(); | |||
return getSession(decodedToken); | |||
} catch (SignatureVerificationException | TokenExpiredException e) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
return null; | |||
} | |||
public boolean isValidToken(Request request) | |||
{ | |||
String token = request.getHeaderValue(Header.AUTHORIZATION); | |||
DecodedJWT decodedToken = null; | |||
if (token != null) | |||
{ | |||
if (token.startsWith("Bearer") && token.length() > 7) | |||
{ | |||
token = token.substring(7); | |||
} | |||
try { | |||
JWTVerifier verifier = JWT.require(this.algorithm).build(); | |||
decodedToken = verifier.verify(token); | |||
} catch (SignatureVerificationException | TokenExpiredException ignored) {} | |||
} | |||
return decodedToken != null; | |||
} | |||
@Override | |||
public long getExpirationDelay() | |||
{ | |||
return expirationDelay; | |||
} | |||
@Override | |||
public void setExpirationDelay(long expirationDelay) | |||
{ | |||
this.expirationDelay = expirationDelay; | |||
} | |||
private Session getSession(DecodedJWT jwt) | |||
{ | |||
long expiresAt = expirationDelay <= 0 ? -1 : LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) * 1000 + expirationDelay; | |||
Session session = new Session(expiresAt, jwt.getToken()); | |||
session.putAt(User.class, gson.fromJson(jwt.getClaim("user").asString(), User.class)); | |||
return session; | |||
} | |||
public Session createSession(User user) | |||
{ | |||
long expiresAt = expirationDelay <= 0 ? -1 : LocalDateTime.now().toEpochSecond(ZoneOffset.UTC) * 1000 + expirationDelay; | |||
String token = JWT.create().withIssuer(paladin.getAppInfo().name()) | |||
.withClaim("user", gson.toJson(user)) | |||
.withIssuedAt(new Date(System.currentTimeMillis())).withExpiresAt(new Date(expiresAt)) | |||
.sign(this.algorithm); | |||
return new Session(expiresAt, token); | |||
} | |||
} |
@@ -8,7 +8,7 @@ import fr.slixe.benchmarks.http.controller.AuthController | |||
import fr.slixe.benchmarks.http.controller.MainController | |||
[ | |||
sessionDuration: TimeUnit.DAYS.toMillis(61), // 2 Months | |||
sessionDuration: TimeUnit.DAYS.toMillis(2), // 2 days | |||
/** | |||
* The app controllers, call them whatever you want to | |||
@@ -1,6 +1,6 @@ | |||
package config; | |||
group '/api', { | |||
group '/api', { | |||
get '/benchmarks', 'main:benchmarks' | |||
post '/submit', 'main:submit' | |||
@@ -9,15 +9,16 @@ group '/api', { | |||
post '/confirm' | |||
post '/delete' | |||
}, [ | |||
action: 'main', | |||
middleware: 'auth' | |||
action: 'main', | |||
middleware: 'auth' | |||
] | |||
} | |||
group '/api/auth', { //not recognized in group /api | |||
group '/api/auth', { | |||
//not recognized in group /api | |||
post '/validate' | |||
post '/login' | |||
post '/logout' | |||
}, [ | |||
action: 'auth' | |||
action: 'auth' | |||
] |