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

Redirect auth token

parent a1896f53
Branches
Tags
3 merge requests!81New stable release,!43Merge dev to master,!36Resolve "Test AMQP"
package at.tuwien.config;
import at.tuwien.auth.AuthEntrypoint;
import at.tuwien.auth.AuthTokenFilter;
import at.tuwien.auth.JwtUtils;
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;
import org.springframework.beans.factory.annotation.Value;
import at.tuwien.auth.AuthTokenFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
......@@ -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.WebSecurityConfigurerAdapter;
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.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
......@@ -21,8 +20,10 @@ import javax.servlet.http.HttpServletResponse;
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${fda.auth.url}")
private String authUrl;
@Bean
public AuthTokenFilter authTokenFilter() {
return new AuthTokenFilter()
}
@Override
protected void configure(HttpSecurity http) throws Exception {
......@@ -45,11 +46,11 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
).and();
/* set permissions on endpoints */
http.authorizeRequests()
/* our public endpoints */
.antMatchers(HttpMethod.GET, "/api/container/**").permitAll()
.antMatchers(HttpMethod.GET, "/api/image/**").permitAll()
/* our private endpoints */
.anyRequest().authenticated();
/* set auth url */
http.formLogin()
.loginProcessingUrl(authUrl);
}
@Bean
......
<template>
<div>
<v-form ref="form" v-model="valid" @submit.prevent="submit">
<v-card>
<v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" />
<v-card-title>
......@@ -11,12 +12,11 @@
color="amber lighten-4 black--text">
Choose an expressive database name and select a database engine.
</v-alert>
<v-form v-model="formValid" autocomplete="off">
<v-text-field
id="database"
v-model="database"
name="database"
label="Database Name"
label="Name *"
:rules="[v => !!v || $t('Required')]"
required />
<v-textarea
......@@ -24,14 +24,14 @@
v-model="description"
name="description"
rows="2"
label="Database Description"
label="Description *"
:rules="[v => !!v || $t('Required')]"
required />
<v-select
id="engine"
v-model="engine"
name="engine"
label="Database Engine"
label="Engine *"
:items="engines"
:item-text="item => `${item.repository}:${item.tag}`"
:rules="[v => !!v || $t('Required')]"
......@@ -43,7 +43,6 @@
name="public"
disabled
label="Public" />
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer />
......@@ -55,13 +54,15 @@
<v-btn
id="createDB"
class="mb-2"
:disabled="!formValid || loading"
:disabled="!valid || loading"
color="primary"
type="submit"
@click="createDB">
Create
</v-btn>
</v-card-actions>
</v-card>
</v-form>
</div>
</template>
......@@ -69,7 +70,7 @@
export default {
data () {
return {
formValid: false,
valid: false,
loading: false,
error: false,
database: null,
......@@ -83,12 +84,18 @@ export default {
computed: {
loadingColor () {
return this.error ? 'red lighten-2' : 'primary'
},
token () {
return this.$store.state.token
}
},
beforeMount () {
this.getImages()
},
methods: {
submit () {
this.$refs.form.validate()
},
cancel () {
this.$parent.$parent.$parent.$parent.createDbDialog = false
},
......@@ -97,7 +104,9 @@ export default {
try {
this.loading = true
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
console.debug('engines', this.engines)
this.loading = false
......@@ -125,13 +134,22 @@ export default {
description: this.description,
repository: this.engine.repository,
tag: this.engine.tag
}, {
headers: { Authorization: `Bearer ${this.token}` }
})
containerId = res.data.id
console.debug('created container', res.data)
this.loading = false
} catch (err) {
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
}
......@@ -139,8 +157,8 @@ export default {
try {
this.loading = true
this.error = false
res = await this.$axios.put(`/api/container/${containerId}`, {
action: 'START'
res = await this.$axios.put(`/api/container/${containerId}`, { action: 'START' }, {
headers: { Authorization: `Bearer ${this.token}` }
})
console.debug('started container', res.data)
} catch (err) {
......@@ -162,6 +180,8 @@ export default {
name: this.database,
description: this.description,
is_public: this.isPublic
}, {
headers: { Authorization: `Bearer ${this.token}` }
})
console.debug('created database', res)
break
......
......@@ -29,11 +29,19 @@
<v-toolbar-title v-text="title" />
<v-spacer />
<v-btn
v-if="!token"
class="mr-2 white--text"
color="blue-grey"
to="/login">
<v-icon left>mdi-login</v-icon> Login
</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>
<template v-slot:activator="{ on, attrs }">
<v-btn
......@@ -44,22 +52,16 @@
</v-btn>
</template>
<v-list>
<v-list-item to="/signup">
<v-list-item-icon>
<v-icon left>mdi-account-plus</v-icon>
</v-list-item-icon>
<v-list-item-title>Create Account</v-list-item-title>
<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-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-menu>
</v-app-bar>
......@@ -68,14 +70,14 @@
<nuxt />
</v-container>
</v-main>
<v-footer v-if="sandbox" padless>
<v-footer padless>
<v-card
flat
tile
width="100%"
class="primary text-center">
<v-card-text class="white--text">
<strong>Sandbox Environment</strong><a href="//github.com/fair-data-austria/dbrepo/issues/new" class="white--text">Report a bug</a>
class="red lighten-1 text-center">
<v-card-text class="black--text">
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>
</v-footer>
......@@ -145,8 +147,8 @@ export default {
availableLocales () {
return this.$i18n.locales.filter(i => i.code !== this.$i18n.locale)
},
sandbox () {
return true
token () {
return this.$store.state.token
},
nextTheme () {
return this.$vuetify.theme.dark ? 'Light' : 'Dark'
......
......@@ -7,7 +7,7 @@
</v-toolbar-title>
<v-spacer />
<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-btn>
</v-toolbar-title>
......@@ -89,9 +89,7 @@ export default {
this.createDbDialog = false
try {
this.loading = true
let res = await this.$axios.get('/api/container/', {
headers: { Authorization: `Bearer ${this.token}` }
})
let res = await this.$axios.get('/api/container/')
this.containers = res.data
console.debug('containers', this.containers)
for (const container of this.containers) {
......
<template>
<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-card-title>
Login
</v-card-title>
<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-col>
<v-col cols="6">
<v-text-field
v-model="loginAccount.username"
autocomplete="off"
required
:rules="[v => !!v || $t('Required')]"
label="Username *" />
</v-col>
</v-row>
<v-row>
<v-col>
<v-col cols="6">
<v-text-field
v-model="loginAccount.password"
autocomplete="off"
type="password"
required
:rules="[v => !!v || $t('Required')]"
label="Password *" />
</v-col>
</v-row>
</v-form>
</v-card-text>
<v-card-actions>
<v-spacer />
<v-btn
id="login"
class="mb-2"
class="mb-2 ml-2"
:disabled="!valid"
color="primary"
type="submit"
@click="login">
Login
</v-btn>
</v-card-actions>
</v-card>
</v-form>
<p v-if="token">Already logged-in</p>
</div>
</template>
......@@ -59,11 +67,17 @@ export default {
computed: {
loadingColor () {
return this.error ? 'red lighten-2' : 'primary'
},
token () {
return this.$store.state.token
}
},
beforeMount () {
},
methods: {
submit () {
this.$refs.form.validate()
},
async login () {
const url = '/api/auth'
try {
......@@ -72,11 +86,15 @@ export default {
console.debug('login user', res.data)
this.$store.commit('SET_TOKEN', res.data.token)
this.$toast.success('Welcome back!')
this.$router.push('/container')
} catch (err) {
console.error('login user failed', err)
this.$toast.error('Failed to login user')
}
this.loading = false
},
signup () {
this.$router.push('/signup')
}
}
}
......
<template>
<div>
<v-form ref="form" v-model="valid" @submit.prevent="submit">
<v-card>
<v-progress-linear v-if="loading" :color="loadingColor" :indeterminate="!error" />
<v-card-title>
......@@ -11,58 +12,62 @@
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.
</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-col>
<v-col cols="6">
<v-text-field
v-model="createAccount.email"
type="email"
autocomplete="off"
required
:rules="[v => !!v || $t('Required')]"
hint="e.g. max.mustermann@work.com"
label="Work E-Mail Address *" />
</v-col>
</v-row>
<v-row>
<v-col>
<v-col cols="6">
<v-text-field
v-model="createAccount.username"
autocomplete="off"
required
:rules="[v => !!v || $t('Required')]"
hint="e.g. mmustermann"
label="Username *" />
</v-col>
</v-row>
<v-row>
<v-col>
<v-col cols="6">
<v-text-field
v-model="createAccount.password"
autocomplete="off"
required
:rules="[v => !!v || $t('Required')]"
type="password"
label="Password *" />
</v-col>
</v-row>
<v-row>
<v-col>
<v-col cols="6">
<v-checkbox
v-model="consent"
required
:rules="[v => !!v || $t('Required')]"
label="I understand the warning and do not use production data" />
</v-col>
</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-actions>
<v-spacer />
<v-btn
class="mb-2"
@click="cancel">
class="mb-2 ml-2">
Cancel
</v-btn>
<v-btn
......@@ -70,11 +75,13 @@
class="mb-2"
:disabled="!valid"
color="primary"
type="submit"
@click="register">
Submit
</v-btn>
</v-card-actions>
</v-card>
</v-form>
</div>
</template>
......@@ -85,6 +92,7 @@ export default {
loading: false,
error: false,
valid: false,
privacy: false,
consent: false,
createAccount: {
username: null,
......@@ -101,6 +109,9 @@ export default {
beforeMount () {
},
methods: {
submit () {
this.$refs.form.validate()
},
async register () {
const url = '/api/user'
try {
......@@ -108,6 +119,7 @@ export default {
const res = await this.$axios.post(url, this.createAccount)
console.debug('create user', res.data)
this.$toast.success('Success. Check your inbox!')
this.$router.push('/login')
} catch (err) {
console.error('create user failed', err)
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