mirror of
https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC.git
synced 2024-12-22 12:30:16 +02:00
MOB-55 Fixed some issues with session management.
This commit is contained in:
parent
13a0a9430f
commit
8b78ddf51a
@ -7,9 +7,10 @@
|
|||||||
|
|
||||||
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
||||||
</div>
|
</div>
|
||||||
<div id="canvas"></div>
|
|
||||||
<div class="justify-content-center d-flex">
|
<div class="justify-content-center d-flex">
|
||||||
|
<div id="canvas"></div>
|
||||||
|
</div>
|
||||||
|
<div class="justify-content-center d-flex">
|
||||||
<button type="button" class="btn loginButton btn-dark" v-on:click="authenticate">
|
<button type="button" class="btn loginButton btn-dark" v-on:click="authenticate">
|
||||||
<div v-if="loading" class="d-flex justify-content-center">
|
<div v-if="loading" class="d-flex justify-content-center">
|
||||||
<div class="spinner-border text-light spinner-border-sm" role="status">
|
<div class="spinner-border text-light spinner-border-sm" role="status">
|
||||||
@ -20,7 +21,7 @@
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group-sm d-flex justify-content-center" role="group" aria-label="Basic radio toggle button group">
|
<div class="btn-group-sm d-flex justify-content-center" v-if="!isAndroidDevice" role="group" aria-label="Basic radio toggle button group">
|
||||||
<input type="radio" class="btn-check" name="btnradio" id="btnCardReader" autocomplete="off" v-on:click="useCardReader">
|
<input type="radio" class="btn-check" name="btnradio" id="btnCardReader" autocomplete="off" v-on:click="useCardReader">
|
||||||
<label class="btn btn-outline-secondary" for="btnCardReader">using ID-card reader</label>
|
<label class="btn btn-outline-secondary" for="btnCardReader">using ID-card reader</label>
|
||||||
|
|
||||||
@ -39,14 +40,11 @@ import router from "@/router";
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'LoginComponent',
|
name: 'LoginComponent',
|
||||||
props: {
|
|
||||||
"csrftoken": String,
|
|
||||||
"csrfHeaderName": String,
|
|
||||||
},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
useAndroidApp: true,
|
useAndroidApp: true,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
challenge: "",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
@ -58,6 +56,8 @@ export default {
|
|||||||
this.useAndroidApp = false;
|
this.useAndroidApp = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
authenticate: async function () {
|
authenticate: async function () {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|
||||||
@ -69,7 +69,6 @@ export default {
|
|||||||
headers: {
|
headers: {
|
||||||
"sessionId": this.$store.getters.getSessionId
|
"sessionId": this.$store.getters.getSessionId
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(options);
|
console.log(options);
|
||||||
@ -78,7 +77,7 @@ export default {
|
|||||||
const response = await webeid.authenticate(options);
|
const response = await webeid.authenticate(options);
|
||||||
console.log("Authentication successful! Response:", response);
|
console.log("Authentication successful! Response:", response);
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.$store.commit("setLoggedIn", true);
|
this.$store.dispatch("setLoggedIn", true);
|
||||||
await router.push("welcome");
|
await router.push("welcome");
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -95,7 +94,14 @@ export default {
|
|||||||
},
|
},
|
||||||
loading() {
|
loading() {
|
||||||
return this.loading;
|
return this.loading;
|
||||||
|
},
|
||||||
|
isAndroidDevice() {
|
||||||
|
return this.$store.getters.getIsAndroid
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
const isAndroid = webeid.isAndroidDevice();
|
||||||
|
this.$store.dispatch("setIsAndroid", isAndroid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
@ -31,23 +31,21 @@ export default {
|
|||||||
fetch("/auth/logout", requestOptions)
|
fetch("/auth/logout", requestOptions)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log(response);
|
console.log(response);
|
||||||
this.$store.commit("setLoggedIn", false);
|
this.$store.dispatch("setLoggedIn", false);
|
||||||
router.push("/");
|
router.push("/");
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.$store.getters.getSessionId == null) {
|
const sessionId = this.$cookie.getCookie("JSESSIONID");
|
||||||
const sessionId = this.$cookie.getCookie("JSESSIONID");
|
this.$store.dispatch("fetchSessionId", sessionId);
|
||||||
this.$store.dispatch("fetchSessionId", sessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
nav {
|
nav {
|
||||||
height: 5vh;
|
height: 7vh;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
@ -1,8 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container container-md d-flex flex-column">
|
<div class="container container-md d-flex flex-column">
|
||||||
<div>
|
<div>
|
||||||
<h3 class="text-center">Congratulations, you logged into the site. Log out to try again.</h3>
|
<h3 class="text-center">Welcome {{ userName }}!</h3>
|
||||||
<p class="text-center">Read more from <a href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
<h4 class="text-center">{{ userIdCode }}</h4>
|
||||||
|
<p class="text-center">You've successfully logged into this site using your ID card.</p>
|
||||||
|
<p class="text-center">Read more from <a
|
||||||
|
href="https://github.com/TanelOrumaa/Estonian-ID-card-mobile-authenticator-POC">here.</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -12,14 +15,46 @@
|
|||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'WelcomeComponent',
|
name: 'WelcomeComponent',
|
||||||
props: {
|
props: {},
|
||||||
"csrftoken": String,
|
methods: {
|
||||||
|
getUserData: async function () {
|
||||||
|
const requestOptions = {
|
||||||
|
method: "GET",
|
||||||
|
headers: {
|
||||||
|
"sessionid": this.$store.getters.getSessionId
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetch("/auth/userData", requestOptions)
|
||||||
|
.then((response) => {
|
||||||
|
let data = response.body;
|
||||||
|
data.getReader().read().then((body) => {
|
||||||
|
let authObject = JSON.parse(new TextDecoder().decode(body.value));
|
||||||
|
this.$store.dispatch("setUserName", authObject.userData.name);
|
||||||
|
let idCode = authObject.userData.idCode.substring(6)
|
||||||
|
console.log(idCode)
|
||||||
|
this.$store.dispatch("setUserIdCode", idCode);
|
||||||
|
});
|
||||||
|
console.log(data);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
isLoggedIn() {
|
isLoggedIn() {
|
||||||
return this.$store.getters.getAuthenticated;
|
return this.$store.getters.getAuthenticated;
|
||||||
|
},
|
||||||
|
userName() {
|
||||||
|
return this.$store.getters.getUserName;
|
||||||
|
},
|
||||||
|
userIdCode() {
|
||||||
|
return this.$store.getters.getUserIdCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
,
|
||||||
|
mounted() {
|
||||||
|
// Get user data.
|
||||||
|
this.getUserData();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ const store = createStore({
|
|||||||
return {
|
return {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
jSessionId: null,
|
jSessionId: null,
|
||||||
|
isAndroid: false,
|
||||||
|
userName: null,
|
||||||
|
userIdCode: null,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mutations: {
|
mutations: {
|
||||||
@ -24,11 +27,32 @@ const store = createStore({
|
|||||||
},
|
},
|
||||||
setSessionId(state, sessionId) {
|
setSessionId(state, sessionId) {
|
||||||
state.jSessionId = sessionId;
|
state.jSessionId = sessionId;
|
||||||
|
},
|
||||||
|
setIsAndroid(state, isAndroid) {
|
||||||
|
state.isAndroid = isAndroid;
|
||||||
|
},
|
||||||
|
setUserName(state, userName) {
|
||||||
|
state.userName = userName;
|
||||||
|
},
|
||||||
|
setIdCode(state, idCode) {
|
||||||
|
state.userIdCode = idCode;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
fetchSessionId(context, sessionId) {
|
fetchSessionId(context, sessionId) {
|
||||||
context.commit("setSessionId", sessionId);
|
context.commit("setSessionId", sessionId);
|
||||||
|
},
|
||||||
|
setLoggedIn(context, isLoggedIn) {
|
||||||
|
context.commit("setLoggedIn", isLoggedIn);
|
||||||
|
},
|
||||||
|
setIsAndroid(context, isAndroid) {
|
||||||
|
context.commit("setIsAndroid", isAndroid);
|
||||||
|
},
|
||||||
|
setUserName(context, userName) {
|
||||||
|
context.commit("setUserName", userName);
|
||||||
|
},
|
||||||
|
setUserIdCode(context, userIdCode) {
|
||||||
|
context.commit("setIdCode", userIdCode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
@ -37,7 +61,16 @@ const store = createStore({
|
|||||||
},
|
},
|
||||||
getSessionId: state => {
|
getSessionId: state => {
|
||||||
return state.jSessionId;
|
return state.jSessionId;
|
||||||
}
|
},
|
||||||
|
getIsAndroid: state => {
|
||||||
|
return state.isAndroid;
|
||||||
|
},
|
||||||
|
getUserName: state => {
|
||||||
|
return state.userName;
|
||||||
|
},
|
||||||
|
getUserIdCode: state => {
|
||||||
|
return state.userIdCode;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
plugins: [createPersistedState()],
|
plugins: [createPersistedState()],
|
||||||
})
|
})
|
||||||
|
@ -8658,16 +8658,33 @@ class WebExtensionService {
|
|||||||
}
|
}
|
||||||
publishMessage(message, timeout) {
|
publishMessage(message, timeout) {
|
||||||
if (message.useAuthApp && message.useAuthApp == true) {
|
if (message.useAuthApp && message.useAuthApp == true) {
|
||||||
if (this.isAndroidDevice()) {
|
if (isAndroidDevice()) {
|
||||||
// Launch auth app.
|
// Launch auth app.
|
||||||
|
console.log("Launching auth app");
|
||||||
this.launchAuthApp(message);
|
this.launchAuthApp(message);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Display QR code.
|
// Display QR code.
|
||||||
this.displayQRCode(message);
|
this.displayQRCode(message);
|
||||||
}
|
}
|
||||||
this.pollForLoginSuccess(message, timeout).then((res) => {
|
console.log("Polling for success.");
|
||||||
console.log(res);
|
this.pollForLoginSuccess(message, timeout).then((req) => {
|
||||||
|
req.on("response", (res) => {
|
||||||
|
if (res.statusCode == 200) {
|
||||||
|
console.log(res.statusCode);
|
||||||
|
window.postMessage({ action: this.getRelevantSuccessAction(message) }, location.origin);
|
||||||
|
res.on("data", (data) => {
|
||||||
|
console.log("HERE WE GOOO:" + data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.removeFromQueue(message.action);
|
||||||
|
return Promise.reject(new ServerRejectedError("Server rejected the authentication."));
|
||||||
|
}
|
||||||
|
}).on("error", () => {
|
||||||
|
this.removeFromQueue(message.action);
|
||||||
|
return Promise.reject(new ServerRejectedError("Server unreachable."));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -8703,35 +8720,29 @@ class WebExtensionService {
|
|||||||
if (!message.getAuthSuccessUrl.startsWith("https://")) {
|
if (!message.getAuthSuccessUrl.startsWith("https://")) {
|
||||||
throw new ProtocolInsecureError(`HTTPS required for getAuthSuccessUrl ${message.getAuthSuccessUrl}`);
|
throw new ProtocolInsecureError(`HTTPS required for getAuthSuccessUrl ${message.getAuthSuccessUrl}`);
|
||||||
}
|
}
|
||||||
|
console.log("Polling for success.");
|
||||||
const headers = message.headers;
|
const headers = message.headers;
|
||||||
|
const url = new URL(message.getAuthSuccessUrl);
|
||||||
|
const host = url.hostname;
|
||||||
|
const port = url.port;
|
||||||
|
const path = url.pathname;
|
||||||
const options = {
|
const options = {
|
||||||
|
host: host,
|
||||||
|
port: port,
|
||||||
|
path: path,
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: headers,
|
headers: headers,
|
||||||
timeout: timeout,
|
timeout: timeout,
|
||||||
};
|
};
|
||||||
return get(message.getAuthSuccessUrl, options).on("error", (e) => {
|
return get(options, (res) => {
|
||||||
console.error(e);
|
console.log("Polling request answered.");
|
||||||
|
}).on("data", (data) => {
|
||||||
|
console.log("DATA: " + data);
|
||||||
|
}).on("error", () => {
|
||||||
|
throw new ServerRejectedError("Authentication failed.");
|
||||||
}).on("timeout", () => {
|
}).on("timeout", () => {
|
||||||
console.error("Timeout");
|
throw new ServerTimeoutError("Server didn't respond in time");
|
||||||
});
|
});
|
||||||
// return await Promise.race([
|
|
||||||
// https.get(message.getAuthSuccessUrl, options, (res) => {
|
|
||||||
// if (res.statusCode < 200 || res.statusCode > 299) {
|
|
||||||
// return reject(new Error(`HTTP status code ${res.statusCode}`))
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// const body = []
|
|
||||||
// res.on('data', (chunk) => body.push(chunk))
|
|
||||||
// res.on('end', () => {
|
|
||||||
// const resString = Buffer.concat(body).toString()
|
|
||||||
// resolve(resString)
|
|
||||||
// })
|
|
||||||
//
|
|
||||||
// this.throwAfterTimeout(
|
|
||||||
// timeout,
|
|
||||||
// new ServerTimeoutError(`server failed to respond in time - GET ${message.getAuthSuccessUrl}`),
|
|
||||||
// ),
|
|
||||||
// ]) as HttpResponse;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
throw new MissingParameterError("getAuthSuccessUrl missing for Android auth app authentication option.");
|
throw new MissingParameterError("getAuthSuccessUrl missing for Android auth app authentication option.");
|
||||||
@ -8746,9 +8757,6 @@ class WebExtensionService {
|
|||||||
setTimeout(() => resolve(), milliseconds);
|
setTimeout(() => resolve(), milliseconds);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
isAndroidDevice() {
|
|
||||||
return navigator.userAgent.toLowerCase().indexOf("android") > -1;
|
|
||||||
}
|
|
||||||
getRelevantAckAction(message) {
|
getRelevantAckAction(message) {
|
||||||
let ackAction;
|
let ackAction;
|
||||||
switch (message.action) {
|
switch (message.action) {
|
||||||
@ -8767,6 +8775,24 @@ class WebExtensionService {
|
|||||||
}
|
}
|
||||||
return ackAction;
|
return ackAction;
|
||||||
}
|
}
|
||||||
|
getRelevantSuccessAction(message) {
|
||||||
|
let ackAction;
|
||||||
|
switch (message.action) {
|
||||||
|
case Action$1.AUTHENTICATE:
|
||||||
|
ackAction = Action$1.AUTHENTICATE_SUCCESS;
|
||||||
|
break;
|
||||||
|
case Action$1.SIGN:
|
||||||
|
ackAction = Action$1.SIGN_SUCCESS;
|
||||||
|
break;
|
||||||
|
case Action$1.STATUS:
|
||||||
|
ackAction = Action$1.STATUS_SUCCESS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ackAction = Action$1.STATUS_SUCCESS;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ackAction;
|
||||||
|
}
|
||||||
onReplyTimeout(pending) {
|
onReplyTimeout(pending) {
|
||||||
var _a;
|
var _a;
|
||||||
console.log("onReplyTimeout", pending.message.action);
|
console.log("onReplyTimeout", pending.message.action);
|
||||||
@ -8776,14 +8802,13 @@ class WebExtensionService {
|
|||||||
onAckTimeout(pending) {
|
onAckTimeout(pending) {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
console.log("onAckTimeout", pending.message.action);
|
console.log("onAckTimeout", pending.message.action);
|
||||||
console.log("Pending message");
|
|
||||||
console.log(pending.message.authApp);
|
|
||||||
if (pending.message.useAuthApp && pending.message.useAuthApp == true) {
|
if (pending.message.useAuthApp && pending.message.useAuthApp == true) {
|
||||||
(_a = pending.reject) === null || _a === void 0 ? void 0 : _a.call(pending, new AuthAppNotInstalledError());
|
(_a = pending.reject) === null || _a === void 0 ? void 0 : _a.call(pending, new AuthAppNotInstalledError());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
(_b = pending.reject) === null || _b === void 0 ? void 0 : _b.call(pending, new ExtensionUnavailableError());
|
(_b = pending.reject) === null || _b === void 0 ? void 0 : _b.call(pending, new ExtensionUnavailableError());
|
||||||
}
|
}
|
||||||
|
this.removeFromQueue(pending.message.action);
|
||||||
clearTimeout(pending.replyTimer);
|
clearTimeout(pending.replyTimer);
|
||||||
}
|
}
|
||||||
getPendingMessage(action) {
|
getPendingMessage(action) {
|
||||||
@ -9021,5 +9046,8 @@ async function sign(options) {
|
|||||||
const result = await webExtensionService.send(message, timeout);
|
const result = await webExtensionService.send(message, timeout);
|
||||||
return result.response;
|
return result.response;
|
||||||
}
|
}
|
||||||
|
function isAndroidDevice() {
|
||||||
|
return navigator.userAgent.toLowerCase().indexOf("android") > -1;
|
||||||
|
}
|
||||||
|
|
||||||
export { Action$1 as Action, ErrorCode$1 as ErrorCode, authenticate, config$1 as config, hasVersionProperties, sign, status };
|
export { Action$1 as Action, ErrorCode$1 as ErrorCode, authenticate, config$1 as config, hasVersionProperties, isAndroidDevice, sign, status };
|
||||||
|
@ -3,14 +3,13 @@ package com.tarkvaratehnika.demobackend.config
|
|||||||
class ApplicationConfiguration {
|
class ApplicationConfiguration {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
// URL for intent, do not edit.
|
|
||||||
val AUTH_APP_LAUNCH_INTENT = "authapp://start/"
|
|
||||||
// Endpoint for challenge.
|
|
||||||
val CHALLENGE_ENDPOINT_URL = "/auth/challenge"
|
|
||||||
// Endpoint for authentication
|
|
||||||
val AUTHENTICATION_ENDPOINT_URL = "/auth/authentication"
|
|
||||||
// URL for application. Use ngrok for HTTPS (or a tool of your own choice) and put the HTTPS link here.
|
// URL for application. Use ngrok for HTTPS (or a tool of your own choice) and put the HTTPS link here.
|
||||||
val WEBSITE_ORIGIN_URL = "https://6b9f-85-253-195-195.ngrok.io"
|
val WEBSITE_ORIGIN_URL = "https://5a0b-85-253-195-195.ngrok.io"
|
||||||
|
|
||||||
|
// Authentication request timeout in seconds.
|
||||||
|
val AUTH_REQUEST_TIMEOUT_MS = 120000
|
||||||
|
|
||||||
|
val USER_ROLE = "USER"
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package com.tarkvaratehnika.demobackend.security
|
package com.tarkvaratehnika.demobackend.config
|
||||||
|
|
||||||
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity
|
||||||
@ -15,9 +15,10 @@ class SecurityConfiguration : WebSecurityConfigurerAdapter() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun configure(http: HttpSecurity) {
|
override fun configure(http: HttpSecurity) {
|
||||||
http.authorizeRequests()?.antMatchers("/**")?.permitAll()
|
http.authorizeRequests()
|
||||||
?.antMatchers("/auth/**")?.permitAll()
|
?.antMatchers("/welcome")?.hasRole("USER")
|
||||||
http.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
|
?.and()
|
||||||
http.csrf().disable()
|
?.sessionManagement()?.sessionCreationPolicy(SessionCreationPolicy.ALWAYS)
|
||||||
|
?.and()?.csrf()?.disable()
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -0,0 +1,107 @@
|
|||||||
|
package com.tarkvaratehnika.demobackend.config
|
||||||
|
|
||||||
|
import com.tarkvaratehnika.demobackend.dto.AuthDto
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.context.annotation.Configuration
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetails
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
class SessionManager {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val LOG = LoggerFactory.getLogger(SessionManager::class.java)
|
||||||
|
|
||||||
|
private val sessionRegistry = HashMap<String, AuthDto>()
|
||||||
|
|
||||||
|
fun registerSession(sessionId: String) {
|
||||||
|
LOG.warn("REGISTERING SESSION $sessionId")
|
||||||
|
if (sessionRegistry.containsKey(sessionId)) {
|
||||||
|
LOG.debug("Session already exists.")
|
||||||
|
} else {
|
||||||
|
sessionRegistry[sessionId] = AuthDto(arrayListOf(), hashMapOf())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addRoleToSession(sessionId: String, role: GrantedAuthority): AuthDto {
|
||||||
|
if (sessionRegistry.containsKey(sessionId)) {
|
||||||
|
val session = sessionRegistry[sessionId]
|
||||||
|
session!!.roles.add(role)
|
||||||
|
return session
|
||||||
|
} else {
|
||||||
|
throw Exception("Session with sessionId: $sessionId does not exist.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Function adds role and userdata specified in authDto to the current session.
|
||||||
|
*/
|
||||||
|
fun addRoleToCurrentSession(authDto: AuthDto) {
|
||||||
|
val securityContext = SecurityContextHolder.getContext()
|
||||||
|
var sessionId = getSessionId()
|
||||||
|
if (sessionId == null) {
|
||||||
|
// No sessionId attached to the session, get one from credentials.
|
||||||
|
sessionId = securityContext.authentication.credentials.toString()
|
||||||
|
}
|
||||||
|
val authentication = UsernamePasswordAuthenticationToken(authDto.userData, sessionId, authDto.roles)
|
||||||
|
securityContext.authentication = authentication
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeRoleFromCurrentSession(headers: Map<String, String>) {
|
||||||
|
val securityContext = SecurityContextHolder.getContext()
|
||||||
|
var sessionId = securityContext.authentication.credentials
|
||||||
|
if (sessionId == null) {
|
||||||
|
// Fallback to when for some reason session object doesn't have sessionId attached.
|
||||||
|
sessionId = getSessionId(headers)
|
||||||
|
}
|
||||||
|
sessionRegistry[sessionId]!!.roles = arrayListOf()
|
||||||
|
val authentication = UsernamePasswordAuthenticationToken(null, sessionId, listOf())
|
||||||
|
securityContext.authentication = authentication
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addUserDataToSession(sessionId: String, name: String, idCode: String): AuthDto {
|
||||||
|
if (sessionRegistry.containsKey(sessionId)) {
|
||||||
|
val session = sessionRegistry[sessionId]
|
||||||
|
session!!.userData["name"] = name
|
||||||
|
session.userData["idCode"] = idCode
|
||||||
|
return session
|
||||||
|
} else {
|
||||||
|
throw Exception("Session with sessionId: $sessionId does not exist.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSessionHasRole(sessionId: String, role: String): Boolean {
|
||||||
|
if (sessionRegistry.containsKey(sessionId)) {
|
||||||
|
if (sessionRegistry[sessionId]!!.roles.contains(SimpleGrantedAuthority(role))) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSessionAuth(sessionId: String?): AuthDto? {
|
||||||
|
if (sessionId == null) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return sessionRegistry[sessionId]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSessionId(headers: Map<String, String>): String? {
|
||||||
|
return headers["sessionid"]
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSessionId(): String? {
|
||||||
|
val context = SecurityContextHolder.getContext()
|
||||||
|
if (context.authentication != null && context.authentication.details != null) {
|
||||||
|
return (context.authentication.details as WebAuthenticationDetails).sessionId
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -3,9 +3,12 @@ package com.tarkvaratehnika.demobackend.config
|
|||||||
import com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
|
import com.github.benmanes.caffeine.jcache.spi.CaffeineCachingProvider
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired
|
||||||
import org.springframework.context.annotation.Bean
|
import org.springframework.context.annotation.Bean
|
||||||
import org.springframework.context.annotation.Configuration
|
import org.springframework.context.annotation.Configuration
|
||||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver
|
||||||
|
import org.springframework.security.core.session.SessionRegistry
|
||||||
|
import org.springframework.security.core.session.SessionRegistryImpl
|
||||||
import org.webeid.security.exceptions.JceException
|
import org.webeid.security.exceptions.JceException
|
||||||
import org.webeid.security.nonce.NonceGenerator
|
import org.webeid.security.nonce.NonceGenerator
|
||||||
import org.webeid.security.nonce.NonceGeneratorBuilder
|
import org.webeid.security.nonce.NonceGeneratorBuilder
|
||||||
@ -13,7 +16,6 @@ import org.webeid.security.validator.AuthTokenValidator
|
|||||||
import org.webeid.security.validator.AuthTokenValidatorBuilder
|
import org.webeid.security.validator.AuthTokenValidatorBuilder
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
import java.net.URL
|
|
||||||
import java.security.KeyStore
|
import java.security.KeyStore
|
||||||
import java.security.KeyStoreException
|
import java.security.KeyStoreException
|
||||||
import java.security.NoSuchAlgorithmException
|
import java.security.NoSuchAlgorithmException
|
||||||
@ -26,12 +28,11 @@ import javax.cache.Cache
|
|||||||
import javax.cache.CacheManager
|
import javax.cache.CacheManager
|
||||||
import javax.cache.Caching
|
import javax.cache.Caching
|
||||||
import javax.cache.configuration.CompleteConfiguration
|
import javax.cache.configuration.CompleteConfiguration
|
||||||
import javax.cache.configuration.FactoryBuilder
|
import javax.cache.configuration.FactoryBuilder.factoryOf
|
||||||
import javax.cache.configuration.MutableConfiguration
|
import javax.cache.configuration.MutableConfiguration
|
||||||
import javax.cache.expiry.CreatedExpiryPolicy
|
import javax.cache.expiry.CreatedExpiryPolicy
|
||||||
import javax.cache.expiry.Duration
|
import javax.cache.expiry.Duration
|
||||||
|
|
||||||
import javax.cache.configuration.FactoryBuilder.factoryOf
|
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
class ValidationConfiguration {
|
class ValidationConfiguration {
|
||||||
@ -43,9 +44,8 @@ class ValidationConfiguration {
|
|||||||
private val CERTS_RESOURCE_PATH = "/certs"
|
private val CERTS_RESOURCE_PATH = "/certs"
|
||||||
private val TRUSTED_CERTIFICATES_JKS = "trusted_certificates.jks"
|
private val TRUSTED_CERTIFICATES_JKS = "trusted_certificates.jks"
|
||||||
private val TRUSTSTORE_PASSWORD = "changeit"
|
private val TRUSTSTORE_PASSWORD = "changeit"
|
||||||
companion object {
|
|
||||||
const val ROLE_USER : String = "ROLE_USER"
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
init {
|
||||||
LOG.warn("Creating new ValidationConfiguration.")
|
LOG.warn("Creating new ValidationConfiguration.")
|
||||||
@ -56,6 +56,8 @@ class ValidationConfiguration {
|
|||||||
return Caching.getCachingProvider(CaffeineCachingProvider::class.java.name).cacheManager
|
return Caching.getCachingProvider(CaffeineCachingProvider::class.java.name).cacheManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
fun nonceCache(): Cache<String, ZonedDateTime>? {
|
fun nonceCache(): Cache<String, ZonedDateTime>? {
|
||||||
val cacheManager: CacheManager = cacheManager()
|
val cacheManager: CacheManager = cacheManager()
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.tarkvaratehnika.demobackend.dto
|
||||||
|
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
|
||||||
|
data class AuthDto(var roles: ArrayList<GrantedAuthority>, var userData: HashMap<String, String>)
|
@ -0,0 +1,5 @@
|
|||||||
|
package com.tarkvaratehnika.demobackend.dto
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty
|
||||||
|
|
||||||
|
class AuthTokenDTO (@JsonProperty("auth-token") val token : String)
|
@ -1,4 +0,0 @@
|
|||||||
package com.tarkvaratehnika.demobackend.security
|
|
||||||
|
|
||||||
class AuthTokenDTO (val token : String, val xsrfToken : String) {
|
|
||||||
}
|
|
@ -23,14 +23,12 @@
|
|||||||
package com.tarkvaratehnika.demobackend.security
|
package com.tarkvaratehnika.demobackend.security
|
||||||
|
|
||||||
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration
|
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration
|
||||||
import com.tarkvaratehnika.demobackend.config.ValidationConfiguration.Companion.ROLE_USER
|
import com.tarkvaratehnika.demobackend.dto.AuthDto
|
||||||
import com.tarkvaratehnika.demobackend.web.rest.AuthenticationController
|
import com.tarkvaratehnika.demobackend.dto.AuthTokenDTO
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.security.authentication.AuthenticationServiceException
|
import org.springframework.security.authentication.AuthenticationServiceException
|
||||||
import org.springframework.security.core.Authentication
|
import org.springframework.security.core.Authentication
|
||||||
import org.springframework.security.core.AuthenticationException
|
import org.springframework.security.core.AuthenticationException
|
||||||
import org.springframework.security.core.GrantedAuthority
|
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||||
import org.springframework.stereotype.Component
|
import org.springframework.stereotype.Component
|
||||||
import org.webeid.security.exceptions.TokenValidationException
|
import org.webeid.security.exceptions.TokenValidationException
|
||||||
@ -44,23 +42,15 @@ object AuthTokenDTOAuthenticationProvider {
|
|||||||
|
|
||||||
private val LOG = LoggerFactory.getLogger(AuthTokenDTOAuthenticationProvider::class.java)
|
private val LOG = LoggerFactory.getLogger(AuthTokenDTOAuthenticationProvider::class.java)
|
||||||
|
|
||||||
|
|
||||||
private val USER_ROLE: GrantedAuthority = SimpleGrantedAuthority(ROLE_USER)
|
|
||||||
|
|
||||||
|
|
||||||
val tokenValidator: AuthTokenValidator = ValidationConfiguration().validator()
|
val tokenValidator: AuthTokenValidator = ValidationConfiguration().validator()
|
||||||
|
|
||||||
@Throws(AuthenticationException::class)
|
@Throws(AuthenticationException::class)
|
||||||
fun authenticate(auth : Authentication) : Authentication {
|
fun authenticate(auth : Authentication, sessionId: String?) : AuthDto {
|
||||||
val authentication = auth as PreAuthenticatedAuthenticationToken
|
val authentication = auth as PreAuthenticatedAuthenticationToken
|
||||||
val token = (authentication.credentials as AuthTokenDTO).token
|
val token = (authentication.credentials as AuthTokenDTO).token
|
||||||
val challenge = (authentication.credentials as AuthTokenDTO)
|
|
||||||
val authorities = arrayListOf<GrantedAuthority>()
|
|
||||||
authorities.add(USER_ROLE)
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val userCertificate: X509Certificate = tokenValidator.validate(token)
|
val userCertificate: X509Certificate = tokenValidator.validate(token)
|
||||||
return WebEidAuthentication.fromCertificate(userCertificate, authorities, "as")
|
return WebEidAuthentication.fromCertificate(userCertificate, sessionId)
|
||||||
} catch (e : TokenValidationException) {
|
} catch (e : TokenValidationException) {
|
||||||
// Validation failed.
|
// Validation failed.
|
||||||
throw AuthenticationServiceException("Token validation failed. " + e.message)
|
throw AuthenticationServiceException("Token validation failed. " + e.message)
|
||||||
|
@ -22,17 +22,27 @@
|
|||||||
|
|
||||||
package com.tarkvaratehnika.demobackend.security
|
package com.tarkvaratehnika.demobackend.security
|
||||||
|
|
||||||
|
import com.tarkvaratehnika.demobackend.config.ApplicationConfiguration
|
||||||
|
import com.tarkvaratehnika.demobackend.config.ApplicationConfiguration.Companion.USER_ROLE
|
||||||
|
import com.tarkvaratehnika.demobackend.config.SessionManager
|
||||||
|
import com.tarkvaratehnika.demobackend.dto.AuthDto
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
import org.webeid.security.certificate.CertificateData
|
import org.webeid.security.certificate.CertificateData
|
||||||
|
|
||||||
import org.springframework.security.core.Authentication
|
import org.springframework.security.core.Authentication
|
||||||
import org.springframework.security.core.GrantedAuthority
|
import org.springframework.security.core.GrantedAuthority
|
||||||
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
|
import org.springframework.security.core.context.SecurityContext
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
|
import org.springframework.security.web.authentication.WebAuthenticationDetails
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||||
|
import org.springframework.web.server.ResponseStatusException
|
||||||
|
import java.io.Serializable
|
||||||
import java.security.cert.X509Certificate
|
import java.security.cert.X509Certificate
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.ThreadLocalRandom
|
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.math.log
|
import kotlin.collections.HashMap
|
||||||
|
|
||||||
class WebEidAuthentication(
|
class WebEidAuthentication(
|
||||||
private val principalName: String,
|
private val principalName: String,
|
||||||
@ -40,39 +50,71 @@ class WebEidAuthentication(
|
|||||||
private val authorities: ArrayList<GrantedAuthority>
|
private val authorities: ArrayList<GrantedAuthority>
|
||||||
) : PreAuthenticatedAuthenticationToken(principalName, idCode, authorities), Authentication {
|
) : PreAuthenticatedAuthenticationToken(principalName, idCode, authorities), Authentication {
|
||||||
|
|
||||||
|
|
||||||
// Companion object is for static functions.
|
// Companion object is for static functions.
|
||||||
companion object {
|
companion object {
|
||||||
|
private val LOG = LoggerFactory.getLogger(WebEidAuthentication::class.java)
|
||||||
private val loggedInUsers = HashMap<String, Authentication>()
|
|
||||||
|
|
||||||
fun fromCertificate(
|
fun fromCertificate(
|
||||||
userCertificate: X509Certificate,
|
userCertificate: X509Certificate,
|
||||||
authorities: ArrayList<GrantedAuthority>,
|
sessionId: String?,
|
||||||
challenge: String
|
): AuthDto {
|
||||||
): Authentication {
|
// Get user data.
|
||||||
val principalName = getPrincipalNameFromCertificate(userCertificate)
|
val name = getPrincipalNameFromCertificate(userCertificate)
|
||||||
val idCode = Objects.requireNonNull(CertificateData.getSubjectIdCode(userCertificate))
|
val idCode = Objects.requireNonNull(CertificateData.getSubjectIdCode(userCertificate))
|
||||||
val authentication = WebEidAuthentication(principalName, idCode, authorities)
|
|
||||||
loggedInUsers[challenge] = authentication
|
// Fetch valid sessionId.
|
||||||
return authentication
|
var methodIndependentSessionId = sessionId
|
||||||
|
if (methodIndependentSessionId == null) {
|
||||||
|
methodIndependentSessionId = SessionManager.getSessionId()
|
||||||
|
if (methodIndependentSessionId == null) {
|
||||||
|
throw Exception("No session")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add role and user data to the AuthDto and return it.
|
||||||
|
SessionManager.addRoleToSession(methodIndependentSessionId, SimpleGrantedAuthority(USER_ROLE))
|
||||||
|
return SessionManager.addUserDataToSession(methodIndependentSessionId, name, idCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function for getting a Spring authentication object by supplying a challenge.
|
* Function for getting a Spring authentication object by supplying a challenge.
|
||||||
* TODO: Figure out a more secure solution in the future.
|
* TODO: Figure out a more secure solution in the future.
|
||||||
*/
|
*/
|
||||||
fun fromChallenge(challenge: String): Authentication? {
|
fun fromSession(headers: HashMap<String, String>): AuthDto {
|
||||||
|
val currentTime = Date()
|
||||||
|
|
||||||
|
// Get sessionId for current session.
|
||||||
|
var sessionId = SessionManager.getSessionId()
|
||||||
|
|
||||||
|
if (sessionId == null) {
|
||||||
|
LOG.warn("SESSION IS NULL")
|
||||||
|
sessionId = SessionManager.getSessionId(headers)
|
||||||
|
if (sessionId == null) {
|
||||||
|
LOG.warn("SESSION IS STILL NULL")
|
||||||
|
throw ResponseStatusException(HttpStatus.FORBIDDEN, "Session ID not found.")
|
||||||
|
}
|
||||||
|
LOG.warn("SESSION IS NOW: " + sessionId)
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentTime.time + ApplicationConfiguration.AUTH_REQUEST_TIMEOUT_MS > Date().time) {
|
||||||
|
Thread.sleep(1000)
|
||||||
|
|
||||||
|
if (SessionManager.getSessionHasRole(sessionId, USER_ROLE)) {
|
||||||
|
// Get AuthDto
|
||||||
|
val auth = SessionManager.getSessionAuth(sessionId)
|
||||||
|
|
||||||
|
// Set role and user data to current session.
|
||||||
|
SessionManager.addRoleToCurrentSession(auth!!)
|
||||||
|
LOG.warn("ROLE ADDED AND LOGGING IN.")
|
||||||
|
return auth
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
// if (ThreadLocalRandom.current().nextFloat() < 0.5f) { // TODO: For testing.
|
// if (ThreadLocalRandom.current().nextFloat() < 0.5f) { // TODO: For testing.
|
||||||
// return null
|
// return null
|
||||||
// }
|
// }
|
||||||
val auth = loggedInUsers[challenge]
|
throw ResponseStatusException(HttpStatus.REQUEST_TIMEOUT, "Token not received in time.")
|
||||||
if (auth != null) {
|
|
||||||
// If challenge is valid, delete the authentication object from the map (so this can only be fetched once).
|
|
||||||
loggedInUsers.remove(challenge)
|
|
||||||
} else {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return auth
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // TODO: DELETE
|
// // TODO: DELETE
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
package com.tarkvaratehnika.demobackend.web.rest
|
package com.tarkvaratehnika.demobackend.web.rest
|
||||||
|
|
||||||
import com.tarkvaratehnika.demobackend.security.AuthTokenDTO
|
import com.tarkvaratehnika.demobackend.config.SessionManager
|
||||||
|
import com.tarkvaratehnika.demobackend.dto.AuthDto
|
||||||
|
import com.tarkvaratehnika.demobackend.dto.AuthTokenDTO
|
||||||
import com.tarkvaratehnika.demobackend.security.AuthTokenDTOAuthenticationProvider
|
import com.tarkvaratehnika.demobackend.security.AuthTokenDTOAuthenticationProvider
|
||||||
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
|
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
import org.springframework.http.HttpStatus
|
import org.springframework.http.HttpStatus
|
||||||
import org.springframework.http.MediaType
|
import org.springframework.http.MediaType
|
||||||
import org.springframework.security.core.Authentication
|
|
||||||
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken
|
||||||
import org.springframework.web.bind.annotation.*
|
import org.springframework.web.bind.annotation.*
|
||||||
import org.springframework.web.server.ResponseStatusException
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("auth")
|
@RequestMapping("auth")
|
||||||
@ -19,30 +19,31 @@ class AuthenticationController {
|
|||||||
|
|
||||||
|
|
||||||
@PostMapping("login", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
|
@PostMapping("login", consumes = [MediaType.APPLICATION_JSON_VALUE], produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
fun authenticate(@RequestBody body : String): Authentication {
|
fun authenticate(@RequestHeader headers: Map<String, String>, @RequestBody body : AuthTokenDTO): AuthDto {
|
||||||
val parts = body.split("\"")
|
|
||||||
val authToken = AuthTokenDTO(parts[3], parts[7])
|
val sessionId = SessionManager.getSessionId(headers)
|
||||||
|
|
||||||
// Create Spring Security Authentication object with supplied token as credentials.
|
// Create Spring Security Authentication object with supplied token as credentials.
|
||||||
val auth = PreAuthenticatedAuthenticationToken(null, authToken)
|
val auth = PreAuthenticatedAuthenticationToken(null, body)
|
||||||
|
|
||||||
// Return authentication object if success.
|
// Return authentication object if success.
|
||||||
return AuthTokenDTOAuthenticationProvider.authenticate(auth)
|
return AuthTokenDTOAuthenticationProvider.authenticate(auth, sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@GetMapping("login", produces = [MediaType.APPLICATION_JSON_VALUE])
|
@GetMapping("login", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
fun getAuthenticated(headers: String) : Authentication? {
|
fun getAuthenticated(@RequestHeader headers: HashMap<String, String>) : AuthDto {
|
||||||
val auth = WebEidAuthentication.fromChallenge("as")
|
return WebEidAuthentication.fromSession(headers)
|
||||||
if (auth == null) {
|
}
|
||||||
throw ResponseStatusException(HttpStatus.FORBIDDEN, "Not allowed.")
|
|
||||||
}
|
@GetMapping("userData", produces = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
return auth
|
fun getUserData(@RequestHeader headers: Map<String, String>) : AuthDto? {
|
||||||
|
return SessionManager.getSessionAuth(SessionManager.getSessionId(headers))
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostMapping("logout", consumes = [MediaType.APPLICATION_JSON_VALUE])
|
@PostMapping("logout", consumes = [MediaType.APPLICATION_JSON_VALUE])
|
||||||
fun logOut(@RequestBody body: String) : HttpStatus? {
|
fun logOut(@RequestHeader headers: Map<String, String>, @RequestBody body: String) : HttpStatus? {
|
||||||
LOG.warn("I WAS HERE")
|
SessionManager.removeRoleFromCurrentSession(headers)
|
||||||
LOG.warn(body)
|
|
||||||
return HttpStatus.ACCEPTED
|
return HttpStatus.ACCEPTED
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,14 +22,24 @@
|
|||||||
|
|
||||||
package com.tarkvaratehnika.demobackend.web.rest
|
package com.tarkvaratehnika.demobackend.web.rest
|
||||||
|
|
||||||
|
import com.tarkvaratehnika.demobackend.config.SessionManager
|
||||||
import com.tarkvaratehnika.demobackend.dto.ChallengeDto
|
import com.tarkvaratehnika.demobackend.dto.ChallengeDto
|
||||||
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
|
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.http.HttpStatus
|
||||||
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
|
||||||
|
import org.springframework.security.core.Authentication
|
||||||
|
import org.springframework.security.core.GrantedAuthority
|
||||||
|
import org.springframework.security.core.authority.SimpleGrantedAuthority
|
||||||
|
import org.springframework.security.core.context.SecurityContextHolder
|
||||||
import org.springframework.web.bind.annotation.GetMapping
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader
|
||||||
import org.springframework.web.bind.annotation.RequestMapping
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
import org.springframework.web.bind.annotation.RestController
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.springframework.web.client.HttpClientErrorException
|
||||||
|
import org.springframework.web.server.ResponseStatusException
|
||||||
import org.webeid.security.nonce.NonceGenerator
|
import org.webeid.security.nonce.NonceGenerator
|
||||||
|
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("auth")
|
@RequestMapping("auth")
|
||||||
class ChallengeController (val nonceGenerator: NonceGenerator) {
|
class ChallengeController (val nonceGenerator: NonceGenerator) {
|
||||||
@ -37,10 +47,30 @@ class ChallengeController (val nonceGenerator: NonceGenerator) {
|
|||||||
private val LOG = LoggerFactory.getLogger(ChallengeController::class.java)
|
private val LOG = LoggerFactory.getLogger(ChallengeController::class.java)
|
||||||
|
|
||||||
@GetMapping("challenge")
|
@GetMapping("challenge")
|
||||||
fun challenge(): ChallengeDto {
|
fun challenge(@RequestHeader headers: Map<String, String>): ChallengeDto {
|
||||||
|
|
||||||
|
val sessionId = SessionManager.getSessionId(headers)
|
||||||
|
|
||||||
|
if (sessionId == null) {
|
||||||
|
LOG.warn("SESSION ID MISSING FOR CHALLENGE")
|
||||||
|
throw ResponseStatusException(HttpStatus.FORBIDDEN, "SessionId missing.")
|
||||||
|
}
|
||||||
|
|
||||||
|
SessionManager.registerSession(sessionId)
|
||||||
|
|
||||||
|
// val context = SecurityContextHolder.getContext()
|
||||||
|
// val authorities = arrayListOf<GrantedAuthority>()
|
||||||
|
// authorities.add(SimpleGrantedAuthority("USER"))
|
||||||
|
// authorities.add(SimpleGrantedAuthority("ROLE_USER"))
|
||||||
|
// val auth = context.authentication
|
||||||
|
//
|
||||||
|
// val newAuth: Authentication =
|
||||||
|
// UsernamePasswordAuthenticationToken(auth.principal, auth.credentials, authorities)
|
||||||
|
// SecurityContextHolder.getContext().authentication = newAuth;
|
||||||
|
|
||||||
|
// SessionManager.createSession(SessionManager.getSessionId(headers))
|
||||||
val challengeDto = ChallengeDto(nonceGenerator.generateAndStoreNonce())
|
val challengeDto = ChallengeDto(nonceGenerator.generateAndStoreNonce())
|
||||||
LOG.warn(challengeDto.nonce)
|
LOG.warn(challengeDto.nonce)
|
||||||
// WebEidAuthentication.addAuth(challengeDto.nonce) // For testing.
|
|
||||||
return challengeDto
|
return challengeDto
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2020, 2021 The Web eID Project
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.tarkvaratehnika.demobackend.web.rest
|
||||||
|
|
||||||
|
import com.tarkvaratehnika.demobackend.config.SessionManager
|
||||||
|
import com.tarkvaratehnika.demobackend.dto.ChallengeDto
|
||||||
|
import com.tarkvaratehnika.demobackend.security.WebEidAuthentication
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping
|
||||||
|
import org.springframework.web.bind.annotation.RequestHeader
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping
|
||||||
|
import org.springframework.web.bind.annotation.RestController
|
||||||
|
import org.webeid.security.nonce.NonceGenerator
|
||||||
|
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("auth")
|
||||||
|
class Test (val nonceGenerator: NonceGenerator) {
|
||||||
|
|
||||||
|
private val LOG = LoggerFactory.getLogger(ChallengeController::class.java)
|
||||||
|
|
||||||
|
@GetMapping("test")
|
||||||
|
fun test(@RequestHeader headers: Map<String, String>): String {
|
||||||
|
return "<h1>JOUUUUUUUU</h1>"
|
||||||
|
}
|
||||||
|
|
||||||
|
@GetMapping("test2")
|
||||||
|
fun test2(@RequestHeader headers: Map<String, String>): String {
|
||||||
|
return "<h1>JOUUUUUUUU22222222222222222</h1>"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user