Skip to content
Snippets Groups Projects
Commit c3324e10 authored by Martin Weise's avatar Martin Weise
Browse files

Redirect auth token

Former-commit-id: ca84303e
parent e53fdeea
No related branches found
No related tags found
1 merge request!42Fixed the query service tests
package at.tuwien.config; package at.tuwien.config;
import at.tuwien.auth.AuthEntrypoint;
import at.tuwien.auth.AuthTokenFilter; import at.tuwien.auth.AuthTokenFilter;
import at.tuwien.auth.JwtUtils; import at.tuwien.auth.JwtUtils;
import at.tuwien.service.impl.UserDetailsServiceImpl; import at.tuwien.service.impl.UserDetailsServiceImpl;
......
package at.tuwien.auth;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
@Component
public class AuthTokenFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
String jwt = parseJwt(request);
if (jwt != null) {
final UserDetails userDetails = userDetailsService.loadUserByUsername(username);
final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterChain.doFilter(request, response);
}
/**
* Parses the token from the HTTP header of the request
*
* @param request The request.
* @return The token.
*/
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7, headerAuth.length());
}
return null;
}
}
\ No newline at end of file
package at.tuwien.config; package at.tuwien.config;
import org.springframework.beans.factory.annotation.Value; import at.tuwien.auth.AuthTokenFilter;
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.http.HttpMethod; import org.springframework.http.HttpMethod;
...@@ -9,7 +9,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity; ...@@ -9,7 +9,6 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter; import org.springframework.web.filter.CorsFilter;
...@@ -21,8 +20,10 @@ import javax.servlet.http.HttpServletResponse; ...@@ -21,8 +20,10 @@ import javax.servlet.http.HttpServletResponse;
@EnableGlobalMethodSecurity(prePostEnabled = true) @EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${fda.auth.url}") @Bean
private String authUrl; public AuthTokenFilter authTokenFilter() {
return new AuthTokenFilter()
}
@Override @Override
protected void configure(HttpSecurity http) throws Exception { protected void configure(HttpSecurity http) throws Exception {
...@@ -45,11 +46,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ...@@ -45,11 +46,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
).and(); ).and();
/* set permissions on endpoints */ /* set permissions on endpoints */
http.authorizeRequests() http.authorizeRequests()
/* our public endpoints */
.antMatchers(HttpMethod.GET, "/api/container/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/image/**").permitAll()
/* our private endpoints */ /* our private endpoints */
.anyRequest().authenticated(); .anyRequest().authenticated();
/* set auth url */
http.formLogin()
.loginProcessingUrl(authUrl);
} }
@Bean @Bean
......
<template> <template>
<div> <div>
<v-form ref="form" v-model="valid" @submit.prevent="submit">
<v-card> <v-card>
<v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" />
<v-card-title> <v-card-title>
...@@ -11,12 +12,11 @@ ...@@ -11,12 +12,11 @@
color="amber lighten-4 black--text"> color="amber lighten-4 black--text">
Choose an expressive database name and select a database engine. Choose an expressive database name and select a database engine.
</v-alert> </v-alert>
<v-form v-model="formValid" autocomplete="off">
<v-text-field <v-text-field
id="database" id="database"
v-model="database" v-model="database"
name="database" name="database"
label="Database Name" label="Name *"
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
required /> required />
<v-textarea <v-textarea
...@@ -24,14 +24,14 @@ ...@@ -24,14 +24,14 @@
v-model="description" v-model="description"
name="description" name="description"
rows="2" rows="2"
label="Database Description" label="Description *"
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
required /> required />
<v-select <v-select
id="engine" id="engine"
v-model="engine" v-model="engine"
name="engine" name="engine"
label="Database Engine" label="Engine *"
:items="engines" :items="engines"
:item-text="item => `${item.repository}:${item.tag}`" :item-text="item => `${item.repository}:${item.tag}`"
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
...@@ -43,7 +43,6 @@ ...@@ -43,7 +43,6 @@
name="public" name="public"
disabled disabled
label="Public" /> label="Public" />
</v-form>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer /> <v-spacer />
...@@ -55,13 +54,15 @@ ...@@ -55,13 +54,15 @@
<v-btn <v-btn
id="createDB" id="createDB"
class="mb-2" class="mb-2"
:disabled="!formValid || loading" :disabled="!valid || loading"
color="primary" color="primary"
type="submit"
@click="createDB"> @click="createDB">
Create Create
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-form>
</div> </div>
</template> </template>
...@@ -69,7 +70,7 @@ ...@@ -69,7 +70,7 @@
export default { export default {
data () { data () {
return { return {
formValid: false, valid: false,
loading: false, loading: false,
error: false, error: false,
database: null, database: null,
...@@ -83,12 +84,18 @@ export default { ...@@ -83,12 +84,18 @@ export default {
computed: { computed: {
loadingColor () { loadingColor () {
return this.error ? 'red lighten-2' : 'primary' return this.error ? 'red lighten-2' : 'primary'
},
token () {
return this.$store.state.token
} }
}, },
beforeMount () { beforeMount () {
this.getImages() this.getImages()
}, },
methods: { methods: {
submit () {
this.$refs.form.validate()
},
cancel () { cancel () {
this.$parent.$parent.$parent.$parent.createDbDialog = false this.$parent.$parent.$parent.$parent.createDbDialog = false
}, },
...@@ -97,7 +104,9 @@ export default { ...@@ -97,7 +104,9 @@ export default {
try { try {
this.loading = true this.loading = true
this.error = false this.error = false
res = await this.$axios.get('/api/image/') res = await this.$axios.get('/api/image/', {
headers: { Authorization: `Bearer ${this.token}` }
})
this.engines = res.data this.engines = res.data
console.debug('engines', this.engines) console.debug('engines', this.engines)
this.loading = false this.loading = false
...@@ -125,13 +134,22 @@ export default { ...@@ -125,13 +134,22 @@ export default {
description: this.description, description: this.description,
repository: this.engine.repository, repository: this.engine.repository,
tag: this.engine.tag tag: this.engine.tag
}, {
headers: { Authorization: `Bearer ${this.token}` }
}) })
containerId = res.data.id containerId = res.data.id
console.debug('created container', res.data) console.debug('created container', res.data)
this.loading = false this.loading = false
} catch (err) { } catch (err) {
this.error = true this.error = true
this.$toast.error('Could not create container. Try another name.') this.loading = false
if (err.status === 401) {
this.$toast.error('Authentication missing')
console.error('permission denied', err)
return
}
console.error('failed to create container', err)
this.$toast.error('Could not create container.')
return return
} }
...@@ -139,8 +157,8 @@ export default { ...@@ -139,8 +157,8 @@ export default {
try { try {
this.loading = true this.loading = true
this.error = false this.error = false
res = await this.$axios.put(`/api/container/${containerId}`, { res = await this.$axios.put(`/api/container/${containerId}`, { action: 'START' }, {
action: 'START' headers: { Authorization: `Bearer ${this.token}` }
}) })
console.debug('started container', res.data) console.debug('started container', res.data)
} catch (err) { } catch (err) {
...@@ -162,6 +180,8 @@ export default { ...@@ -162,6 +180,8 @@ export default {
name: this.database, name: this.database,
description: this.description, description: this.description,
is_public: this.isPublic is_public: this.isPublic
}, {
headers: { Authorization: `Bearer ${this.token}` }
}) })
console.debug('created database', res) console.debug('created database', res)
break break
......
...@@ -29,11 +29,19 @@ ...@@ -29,11 +29,19 @@
<v-toolbar-title v-text="title" /> <v-toolbar-title v-text="title" />
<v-spacer /> <v-spacer />
<v-btn <v-btn
v-if="!token"
class="mr-2 white--text" class="mr-2 white--text"
color="blue-grey" color="blue-grey"
to="/login"> to="/login">
<v-icon left>mdi-login</v-icon> Login <v-icon left>mdi-login</v-icon> Login
</v-btn> </v-btn>
<v-btn
v-if="!token"
class="mr-2 white--text"
color="primary"
to="/signup">
<v-icon left>mdi-account-plus</v-icon> Signup
</v-btn>
<v-menu bottom offset-y left> <v-menu bottom offset-y left>
<template v-slot:activator="{ on, attrs }"> <template v-slot:activator="{ on, attrs }">
<v-btn <v-btn
...@@ -44,22 +52,16 @@ ...@@ -44,22 +52,16 @@
</v-btn> </v-btn>
</template> </template>
<v-list> <v-list>
<v-list-item to="/signup"> <v-list-item
<v-list-item-icon> v-for="locale in availableLocales"
<v-icon left>mdi-account-plus</v-icon> :key="locale.code"
</v-list-item-icon> :to="switchLocalePath(locale.code)">
<v-list-item-title>Create Account</v-list-item-title> <v-list-item-title>{{ locale.name }}</v-list-item-title>
</v-list-item>
<v-list-item
@click="switchTheme()">
{{ nextTheme }} Theme
</v-list-item> </v-list-item>
<!-- <v-list-item-->
<!-- v-for="locale in availableLocales"-->
<!-- :key="locale.code"-->
<!-- :to="switchLocalePath(locale.code)">-->
<!-- <v-list-item-title>{{ locale.name }}</v-list-item-title>-->
<!-- </v-list-item>-->
<!-- <v-list-item-->
<!-- @click="switchTheme()">-->
<!-- {{ nextTheme }} Theme-->
<!-- </v-list-item>-->
</v-list> </v-list>
</v-menu> </v-menu>
</v-app-bar> </v-app-bar>
...@@ -68,14 +70,14 @@ ...@@ -68,14 +70,14 @@
<nuxt /> <nuxt />
</v-container> </v-container>
</v-main> </v-main>
<v-footer v-if="sandbox" padless> <v-footer padless>
<v-card <v-card
flat flat
tile tile
width="100%" width="100%"
class="primary text-center"> class="red lighten-1 text-center">
<v-card-text class="white--text"> <v-card-text class="black--text">
<strong>Sandbox Environment</strong><a href="//github.com/fair-data-austria/dbrepo/issues/new" class="white--text">Report a bug</a> This is a <strong>TEST</strong> environment, do not use production/confidential data!<a href="//github.com/fair-data-austria/dbrepo/issues/new" class="black--text">Report a bug</a>
</v-card-text> </v-card-text>
</v-card> </v-card>
</v-footer> </v-footer>
...@@ -145,8 +147,8 @@ export default { ...@@ -145,8 +147,8 @@ export default {
availableLocales () { availableLocales () {
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale) return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
}, },
sandbox () { token () {
return true return this.$store.state.token
}, },
nextTheme () { nextTheme () {
return this.$vuetify.theme.dark ? 'Light' : 'Dark' return this.$vuetify.theme.dark ? 'Light' : 'Dark'
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
</v-toolbar-title> </v-toolbar-title>
<v-spacer /> <v-spacer />
<v-toolbar-title> <v-toolbar-title>
<v-btn color="primary" @click.stop="createDbDialog = true"> <v-btn v-if="token" color="primary" @click.stop="createDbDialog = true">
<v-icon left>mdi-plus</v-icon> Database <v-icon left>mdi-plus</v-icon> Database
</v-btn> </v-btn>
</v-toolbar-title> </v-toolbar-title>
...@@ -89,9 +89,7 @@ export default { ...@@ -89,9 +89,7 @@ export default {
this.createDbDialog = false this.createDbDialog = false
try { try {
this.loading = true this.loading = true
let res = await this.$axios.get('/api/container/', { let res = await this.$axios.get('/api/container/')
headers: { Authorization: `Bearer ${this.token}` }
})
this.containers = res.data this.containers = res.data
console.debug('containers', this.containers) console.debug('containers', this.containers)
for (const container of this.containers) { for (const container of this.containers) {
......
<template> <template>
<div> <div>
<v-card> <v-form ref="form" v-model="valid" @submit.prevent="submit">
<v-card v-if="!token">
<v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" />
<v-card-title> <v-card-title>
Login Login
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<v-form ref="form" v-model="valid"> <v-alert
border="left"
color="amber lighten-4 black--text">
If you need an account, create one <a @click="signup">here</a>.
</v-alert>
<v-row> <v-row>
<v-col> <v-col cols="6">
<v-text-field <v-text-field
v-model="loginAccount.username" v-model="loginAccount.username"
autocomplete="off" autocomplete="off"
required
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
label="Username *" /> label="Username *" />
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col> <v-col cols="6">
<v-text-field <v-text-field
v-model="loginAccount.password" v-model="loginAccount.password"
autocomplete="off" autocomplete="off"
type="password" type="password"
required
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
label="Password *" /> label="Password *" />
</v-col> </v-col>
</v-row> </v-row>
</v-form>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer />
<v-btn <v-btn
id="login" id="login"
class="mb-2" class="mb-2 ml-2"
:disabled="!valid" :disabled="!valid"
color="primary" color="primary"
type="submit"
@click="login"> @click="login">
Login Login
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-form>
<p v-if="token">Already logged-in</p>
</div> </div>
</template> </template>
...@@ -59,11 +67,17 @@ export default { ...@@ -59,11 +67,17 @@ export default {
computed: { computed: {
loadingColor () { loadingColor () {
return this.error ? 'red lighten-2' : 'primary' return this.error ? 'red lighten-2' : 'primary'
},
token () {
return this.$store.state.token
} }
}, },
beforeMount () { beforeMount () {
}, },
methods: { methods: {
submit () {
this.$refs.form.validate()
},
async login () { async login () {
const url = '/api/auth' const url = '/api/auth'
try { try {
...@@ -72,11 +86,15 @@ export default { ...@@ -72,11 +86,15 @@ export default {
console.debug('login user', res.data) console.debug('login user', res.data)
this.$store.commit('SET_TOKEN', res.data.token) this.$store.commit('SET_TOKEN', res.data.token)
this.$toast.success('Welcome back!') this.$toast.success('Welcome back!')
this.$router.push('/container')
} catch (err) { } catch (err) {
console.error('login user failed', err) console.error('login user failed', err)
this.$toast.error('Failed to login user') this.$toast.error('Failed to login user')
} }
this.loading = false this.loading = false
},
signup () {
this.$router.push('/signup')
} }
} }
} }
......
<template> <template>
<div> <div>
<v-form ref="form" v-model="valid" @submit.prevent="submit">
<v-card> <v-card>
<v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" /> <v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" />
<v-card-title> <v-card-title>
...@@ -11,58 +12,62 @@ ...@@ -11,58 +12,62 @@
color="amber lighten-4 black--text"> color="amber lighten-4 black--text">
Before you can use the repository sandbox, you will need to <i>confirm</i> your email address, make sure to check your spam folder. Before you can use the repository sandbox, you will need to <i>confirm</i> your email address, make sure to check your spam folder.
</v-alert> </v-alert>
<v-alert
border="left"
color="red lighten-1 black--text">
This is a <strong>TEST</strong> environment, do not use production/confidential data!
</v-alert>
<v-form ref="form" v-model="valid">
<v-row> <v-row>
<v-col> <v-col cols="6">
<v-text-field <v-text-field
v-model="createAccount.email" v-model="createAccount.email"
type="email" type="email"
autocomplete="off" autocomplete="off"
required
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
hint="e.g. max.mustermann@work.com" hint="e.g. max.mustermann@work.com"
label="Work E-Mail Address *" /> label="Work E-Mail Address *" />
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col> <v-col cols="6">
<v-text-field <v-text-field
v-model="createAccount.username" v-model="createAccount.username"
autocomplete="off" autocomplete="off"
required
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
hint="e.g. mmustermann" hint="e.g. mmustermann"
label="Username *" /> label="Username *" />
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col> <v-col cols="6">
<v-text-field <v-text-field
v-model="createAccount.password" v-model="createAccount.password"
autocomplete="off" autocomplete="off"
required
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
type="password" type="password"
label="Password *" /> label="Password *" />
</v-col> </v-col>
</v-row> </v-row>
<v-row> <v-row>
<v-col> <v-col cols="6">
<v-checkbox <v-checkbox
v-model="consent" v-model="consent"
required
:rules="[v => !!v || $t('Required')]" :rules="[v => !!v || $t('Required')]"
label="I understand the warning and do not use production data" /> label="I understand the warning and do not use production data" />
</v-col> </v-col>
</v-row> </v-row>
</v-form> <v-row>
<v-col cols="6">
<v-checkbox
v-model="privacy"
required
:rules="[v => !!v || $t('Required')]"
label="I have read and accept the privacy statement" />
</v-col>
</v-row>
</v-card-text> </v-card-text>
<v-card-actions> <v-card-actions>
<v-spacer />
<v-btn <v-btn
class="mb-2" class="mb-2 ml-2">
@click="cancel">
Cancel Cancel
</v-btn> </v-btn>
<v-btn <v-btn
...@@ -70,11 +75,13 @@ ...@@ -70,11 +75,13 @@
class="mb-2" class="mb-2"
:disabled="!valid" :disabled="!valid"
color="primary" color="primary"
type="submit"
@click="register"> @click="register">
Submit Submit
</v-btn> </v-btn>
</v-card-actions> </v-card-actions>
</v-card> </v-card>
</v-form>
</div> </div>
</template> </template>
...@@ -85,6 +92,7 @@ export default { ...@@ -85,6 +92,7 @@ export default {
loading: false, loading: false,
error: false, error: false,
valid: false, valid: false,
privacy: false,
consent: false, consent: false,
createAccount: { createAccount: {
username: null, username: null,
...@@ -101,6 +109,9 @@ export default { ...@@ -101,6 +109,9 @@ export default {
beforeMount () { beforeMount () {
}, },
methods: { methods: {
submit () {
this.$refs.form.validate()
},
async register () { async register () {
const url = '/api/user' const url = '/api/user'
try { try {
...@@ -108,6 +119,7 @@ export default { ...@@ -108,6 +119,7 @@ export default {
const res = await this.$axios.post(url, this.createAccount) const res = await this.$axios.post(url, this.createAccount)
console.debug('create user', res.data) console.debug('create user', res.data)
this.$toast.success('Success. Check your inbox!') this.$toast.success('Success. Check your inbox!')
this.$router.push('/login')
} catch (err) { } catch (err) {
console.error('create user failed', err) console.error('create user failed', err)
this.$toast.error('Failed to create user') this.$toast.error('Failed to create user')
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment