Skip to content
Snippets Groups Projects
Verified Commit b5ce65fc authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Now use vuex to manage state for authentication

parent df94ae37
No related branches found
No related tags found
No related merge requests found
Showing with 137 additions and 179 deletions
import logger from '@/logging'
import config from '@/config'
import cache from '@/cache'
import Vue from 'vue'
import favoriteTracks from '@/favorites/tracks'
// URL and endpoint constants
const LOGIN_URL = config.API_URL + 'token/'
const USER_PROFILE_URL = config.API_URL + 'users/users/me/'
// const SIGNUP_URL = API_URL + 'users/'
let userData = {
authenticated: false,
username: '',
availablePermissions: {},
profile: {}
}
let auth = {
// Send a request to the login URL and save the returned JWT
login (context, creds, redirect, onError) {
return context.$http.post(LOGIN_URL, creds).then(response => {
logger.default.info('Successfully logged in as', creds.username)
cache.set('token', response.data.token)
cache.set('username', creds.username)
this.user.authenticated = true
this.user.username = creds.username
this.connect()
// Redirect to a specified route
if (redirect) {
context.$router.push(redirect)
}
}, response => {
logger.default.error('Error while logging in', response.data)
if (onError) {
onError(response)
}
})
},
// To log out, we just need to remove the token
logout () {
cache.clear()
this.user.authenticated = false
logger.default.info('Log out, goodbye!')
},
checkAuth () {
logger.default.info('Checking authentication...')
var jwt = this.getAuthToken()
var username = cache.get('username')
if (jwt) {
this.user.authenticated = true
this.user.username = username
logger.default.info('Logged back in as ' + username)
this.connect()
} else {
logger.default.info('Anonymous user')
this.user.authenticated = false
}
},
getAuthToken () {
return cache.get('token')
},
// The object to be passed as a header for authenticated requests
getAuthHeader () {
return 'JWT ' + this.getAuthToken()
},
fetchProfile () {
let resource = Vue.resource(USER_PROFILE_URL)
return resource.get({}).then((response) => {
logger.default.info('Successfully fetched user profile')
return response.data
}, (response) => {
logger.default.info('Error while fetching user profile')
})
},
connect () {
// called once user has logged in successfully / reauthenticated
// e.g. after a page refresh
let self = this
this.fetchProfile().then(data => {
Vue.set(self.user, 'profile', data)
Object.keys(data.permissions).forEach(function (key) {
// this makes it easier to check for permissions in templates
Vue.set(self.user.availablePermissions, key, data.permissions[String(key)].status)
})
})
favoriteTracks.fetch()
}
}
Vue.set(auth, 'user', userData)
export default auth
......@@ -28,8 +28,8 @@
<div class="tabs">
<div class="ui bottom attached active tab" data-tab="library">
<div class="ui inverted vertical fluid menu">
<router-link class="item" v-if="auth.user.authenticated" :to="{name: 'profile', params: {username: auth.user.username}}"><i class="user icon"></i> Logged in as {{ auth.user.username }}</router-link>
<router-link class="item" v-if="auth.user.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i> Logout</router-link>
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'profile', params: {username: $store.state.auth.username}}"><i class="user icon"></i> Logged in as {{ $store.state.auth.username }}</router-link>
<router-link class="item" v-if="$store.state.auth.authenticated" :to="{name: 'logout'}"><i class="sign out icon"></i> Logout</router-link>
<router-link class="item" v-else :to="{name: 'login'}"><i class="sign in icon"></i> Login</router-link>
<router-link class="item" :to="{path: '/library'}"><i class="sound icon"> </i>Browse library</router-link>
<router-link class="item" :to="{path: '/favorites'}"><i class="heart icon"></i> Favorites</router-link>
......@@ -97,7 +97,6 @@ import Player from '@/components/audio/Player'
import favoriteTracks from '@/favorites/tracks'
import Logo from '@/components/Logo'
import SearchBar from '@/components/audio/SearchBar'
import auth from '@/auth'
import backend from '@/audio/backend'
import draggable from 'vuedraggable'
......@@ -113,7 +112,6 @@ export default {
},
data () {
return {
auth: auth,
backend: backend,
favoriteTracks
}
......
......@@ -12,7 +12,6 @@
<script>
import jQuery from 'jquery'
import config from '@/config'
import auth from '@/auth'
import router from '@/router'
const SEARCH_URL = config.API_URL + 'search?query={query}'
......@@ -27,7 +26,7 @@ export default {
},
apiSettings: {
beforeXHR: function (xhrObject) {
xhrObject.setRequestHeader('Authorization', auth.getAuthHeader())
xhrObject.setRequestHeader('Authorization', this.$store.getters['auth/header'])
return xhrObject
},
onResponse: function (initialResponse) {
......
......@@ -15,7 +15,6 @@
<script>
import {mapState} from 'vuex'
import backend from '@/audio/backend'
import auth from '@/auth'
import url from '@/utils/url'
// import logger from '@/logging'
......@@ -40,12 +39,12 @@ export default {
return null
}
let path = backend.absoluteUrl(file.path)
if (auth.user.authenticated) {
if (this.$store.state.auth.authenticated) {
// we need to send the token directly in url
// so authentication can be checked by the backend
// because for audio files we cannot use the regular Authentication
// header
path = url.updateQueryString(path, 'jwt', auth.getAuthToken())
path = url.updateQueryString(path, 'jwt', this.$store.state.auth.token)
}
return path
}
......
......@@ -58,9 +58,9 @@
Keep your PRIVATE_TOKEN secret as it gives access to your account.
</div>
<pre>
export PRIVATE_TOKEN="{{ auth.getAuthToken ()}}"
export PRIVATE_TOKEN="{{ $store.state.auth.token ()}}"
<template v-for="track in tracks"><template v-if="track.files.length > 0">
curl -G -o "{{ track.files[0].filename }}" <template v-if="auth.user.authenticated">--header "Authorization: JWT $PRIVATE_TOKEN"</template> "{{ backend.absoluteUrl(track.files[0].path) }}"</template></template>
curl -G -o "{{ track.files[0].filename }}" <template v-if="$store.state.auth.authenticated">--header "Authorization: JWT $PRIVATE_TOKEN"</template> "{{ backend.absoluteUrl(track.files[0].path) }}"</template></template>
</pre>
</div>
</div>
......@@ -83,7 +83,6 @@ curl -G -o "{{ track.files[0].filename }}" <template v-if="auth.user.authenticat
<script>
import backend from '@/audio/backend'
import auth from '@/auth'
import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
import PlayButton from '@/components/audio/PlayButton'
......@@ -102,7 +101,6 @@ export default {
data () {
return {
backend: backend,
auth: auth,
showDownloadModal: false
}
}
......
......@@ -39,12 +39,11 @@
</template>
<script>
import auth from '@/auth'
export default {
name: 'login',
props: {
next: {type: String}
next: {type: String, default: '/'}
},
data () {
return {
......@@ -72,14 +71,17 @@ export default {
}
// We need to pass the component's this context
// to properly make use of http in the auth service
auth.login(this, credentials, {path: this.next}, function (response) {
// error callback
if (response.status === 400) {
self.error = 'invalid_credentials'
} else {
self.error = 'unknown_error'
this.$store.dispatch('auth/login', {
credentials,
next: this.next,
onError: response => {
if (response.status === 400) {
self.error = 'invalid_credentials'
} else {
self.error = 'unknown_error'
}
}
}).then((response) => {
}).then(e => {
self.isLoading = false
})
}
......
......@@ -3,8 +3,8 @@
<div class="ui vertical stripe segment">
<div class="ui small text container">
<h2>Are you sure you want to log out?</h2>
<p>You are currently logged in as {{ auth.user.username }}</p>
<button class="ui button" @click="logout">Yes, log me out!</button>
<p>You are currently logged in as {{ $store.state.auth.username }}</p>
<button class="ui button" @click="$store.dispatch('auth/logout')">Yes, log me out!</button>
</form>
</div>
</div>
......@@ -12,23 +12,8 @@
</template>
<script>
import auth from '@/auth'
export default {
name: 'logout',
data () {
return {
// We need to initialize the component with any
// properties that will be used in it
auth: auth
}
},
methods: {
logout () {
auth.logout()
this.$router.push({name: 'index'})
}
}
name: 'logout'
}
</script>
......
......@@ -3,17 +3,17 @@
<div v-if="isLoading" class="ui vertical segment">
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
</div>
<template v-if="profile">
<template v-if="$store.state.auth.profile">
<div :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']">
<h2 class="ui center aligned icon header">
<i class="circular inverted user green icon"></i>
<div class="content">
{{ profile.username }}
{{ $store.state.auth.profile.username }}
<div class="sub header">Registered since {{ signupDate }}</div>
</div>
</h2>
<div class="ui basic green label">this is you!</div>
<div v-if="profile.is_staff" class="ui yellow label">
<div v-if="$store.state.auth.profile.is_staff" class="ui yellow label">
<i class="star icon"></i>
Staff member
</div>
......@@ -23,35 +23,21 @@
</template>
<script>
import auth from '@/auth'
var dateFormat = require('dateformat')
const dateFormat = require('dateformat')
export default {
name: 'login',
props: ['username'],
data () {
return {
profile: null
}
},
created () {
this.fetchProfile()
},
methods: {
fetchProfile () {
let self = this
auth.fetchProfile().then(data => {
self.profile = data
})
}
this.$store.dispatch('auth/fetchProfile')
},
computed: {
signupDate () {
let d = new Date(this.profile.date_joined)
let d = new Date(this.$store.state.auth.profile.date_joined)
return dateFormat(d, 'longDate')
},
isLoading () {
return !this.profile
return !this.$store.state.auth.profile
}
}
}
......
......@@ -4,8 +4,8 @@
<router-link class="ui item" to="/library" exact>Browse</router-link>
<router-link class="ui item" to="/library/artists" exact>Artists</router-link>
<div class="ui secondary right menu">
<router-link v-if="auth.user.availablePermissions['import.launch']" class="ui item" to="/library/import/launch" exact>Import</router-link>
<router-link v-if="auth.user.availablePermissions['import.launch']" class="ui item" to="/library/import/batches">Import batches</router-link>
<router-link v-if="$store.state.auth.availablePermissions['import.launch']" class="ui item" to="/library/import/launch" exact>Import</router-link>
<router-link v-if="$store.state.auth.availablePermissions['import.launch']" class="ui item" to="/library/import/batches">Import batches</router-link>
</div>
</div>
<router-view :key="$route.fullPath"></router-view>
......@@ -14,15 +14,8 @@
<script>
import auth from '@/auth'
export default {
name: 'library',
data: function () {
return {
auth
}
}
name: 'library'
}
</script>
......
......@@ -61,7 +61,6 @@
<script>
import auth from '@/auth'
import url from '@/utils/url'
import logger from '@/logging'
import backend from '@/audio/backend'
......@@ -124,8 +123,8 @@ export default {
downloadUrl () {
if (this.track.files.length > 0) {
let u = backend.absoluteUrl(this.track.files[0].path)
if (auth.user.authenticated) {
u = url.updateQueryString(u, 'jwt', auth.getAuthToken())
if (this.$store.state.auth.authenticated) {
u = url.updateQueryString(u, 'jwt', this.$store.state.auth.token)
}
return u
}
......
......@@ -23,7 +23,6 @@
<script>
import jQuery from 'jquery'
import config from '@/config'
import auth from '@/auth'
export default {
props: {
......@@ -66,7 +65,7 @@ export default {
},
apiSettings: {
beforeXHR: function (xhrObject, s) {
xhrObject.setRequestHeader('Authorization', auth.getAuthHeader())
xhrObject.setRequestHeader('Authorization', this.$store.getters['auth/header'])
return xhrObject
},
onResponse: function (initialResponse) {
......
......@@ -9,7 +9,6 @@ import Vue from 'vue'
import App from './App'
import router from './router'
import VueResource from 'vue-resource'
import auth from './auth'
import VueLazyload from 'vue-lazyload'
import store from './store'
......@@ -26,8 +25,8 @@ Vue.config.productionTip = false
Vue.http.interceptors.push(function (request, next) {
// modify headers
if (auth.user.authenticated) {
request.headers.set('Authorization', auth.getAuthHeader())
if (store.state.auth.authenticated) {
request.headers.set('Authorization', store.getters['auth/header'])
}
next(function (response) {
// redirect to login form when we get unauthorized response from server
......@@ -38,7 +37,7 @@ Vue.http.interceptors.push(function (request, next) {
})
})
auth.checkAuth()
store.dispatch('auth/check')
/* eslint-disable no-new */
new Vue({
el: '#app',
......
import Vue from 'vue'
import config from '@/config'
import logger from '@/logging'
import cache from '@/cache'
import router from '@/router'
// import favoriteTracks from '@/favorites/tracks'
const LOGIN_URL = config.API_URL + 'token/'
const USER_PROFILE_URL = config.API_URL + 'users/users/me/'
export default {
namespaced: true,
state: {
authenticated: false,
username: '',
availablePermissions: {},
profile: null,
token: ''
},
getters: {
header: state => {
return 'JWT ' + state.token
}
},
mutations: {
profile: (state, value) => {
state.profile = value
},
authenticated: (state, value) => {
state.authenticated = value
},
username: (state, value) => {
state.username = value
},
token: (state, value) => {
state.token = value
}
},
actions: {
// Send a request to the login URL and save the returned JWT
login ({commit, dispatch, state}, {next, credentials, onError}) {
let resource = Vue.resource(LOGIN_URL)
return resource.save({}, credentials).then(response => {
logger.default.info('Successfully logged in as', credentials.username)
commit('token', response.data.token)
cache.set('token', response.data.token)
commit('username', credentials.username)
cache.set('username', credentials.username)
commit('authenticated', true)
dispatch('fetchProfile')
// Redirect to a specified route
router.push(next)
}, response => {
logger.default.error('Error while logging in', response.data)
onError(response)
})
},
logout ({commit}) {
cache.clear()
commit('authenticated', false)
commit('profile', null)
logger.default.info('Log out, goodbye!')
router.push({name: 'index'})
},
check ({commit, dispatch, state}) {
logger.default.info('Checking authentication...')
var jwt = cache.get('token')
var username = cache.get('username')
if (jwt) {
commit('authenticated', true)
commit('username', username)
commit('token', jwt)
logger.default.info('Logged back in as ' + username)
dispatch('fetchProfile')
} else {
logger.default.info('Anonymous user')
commit('authenticated', false)
}
},
fetchProfile ({commit, state}) {
let resource = Vue.resource(USER_PROFILE_URL)
return resource.get({}).then((response) => {
logger.default.info('Successfully fetched user profile')
let data = response.data
commit('profile', data)
// favoriteTracks.fetch()
console.log('AFTER')
Object.keys(data.permissions).forEach(function (key) {
// this makes it easier to check for permissions in templates
state.availablePermissions[key] = data.permissions[String(key)].status
})
return response.data
}, (response) => {
logger.default.info('Error while fetching user profile')
})
}
}
}
import Vue from 'vue'
import Vuex from 'vuex'
import auth from './auth'
import queue from './queue'
import radios from './radios'
import player from './player'
......@@ -9,6 +10,7 @@ Vue.use(Vuex)
export default new Vuex.Store({
modules: {
auth,
queue,
radios,
player
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment