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
Branches
Tags
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 @@ ...@@ -28,8 +28,8 @@
<div class="tabs"> <div class="tabs">
<div class="ui bottom attached active tab" data-tab="library"> <div class="ui bottom attached active tab" data-tab="library">
<div class="ui inverted vertical fluid menu"> <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="$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="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: '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" 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: '/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> <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' ...@@ -97,7 +97,6 @@ import Player from '@/components/audio/Player'
import favoriteTracks from '@/favorites/tracks' import favoriteTracks from '@/favorites/tracks'
import Logo from '@/components/Logo' import Logo from '@/components/Logo'
import SearchBar from '@/components/audio/SearchBar' import SearchBar from '@/components/audio/SearchBar'
import auth from '@/auth'
import backend from '@/audio/backend' import backend from '@/audio/backend'
import draggable from 'vuedraggable' import draggable from 'vuedraggable'
...@@ -113,7 +112,6 @@ export default { ...@@ -113,7 +112,6 @@ export default {
}, },
data () { data () {
return { return {
auth: auth,
backend: backend, backend: backend,
favoriteTracks favoriteTracks
} }
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
<script> <script>
import jQuery from 'jquery' import jQuery from 'jquery'
import config from '@/config' import config from '@/config'
import auth from '@/auth'
import router from '@/router' import router from '@/router'
const SEARCH_URL = config.API_URL + 'search?query={query}' const SEARCH_URL = config.API_URL + 'search?query={query}'
...@@ -27,7 +26,7 @@ export default { ...@@ -27,7 +26,7 @@ export default {
}, },
apiSettings: { apiSettings: {
beforeXHR: function (xhrObject) { beforeXHR: function (xhrObject) {
xhrObject.setRequestHeader('Authorization', auth.getAuthHeader()) xhrObject.setRequestHeader('Authorization', this.$store.getters['auth/header'])
return xhrObject return xhrObject
}, },
onResponse: function (initialResponse) { onResponse: function (initialResponse) {
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
<script> <script>
import {mapState} from 'vuex' import {mapState} from 'vuex'
import backend from '@/audio/backend' import backend from '@/audio/backend'
import auth from '@/auth'
import url from '@/utils/url' import url from '@/utils/url'
// import logger from '@/logging' // import logger from '@/logging'
...@@ -40,12 +39,12 @@ export default { ...@@ -40,12 +39,12 @@ export default {
return null return null
} }
let path = backend.absoluteUrl(file.path) 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 // we need to send the token directly in url
// so authentication can be checked by the backend // so authentication can be checked by the backend
// because for audio files we cannot use the regular Authentication // because for audio files we cannot use the regular Authentication
// header // header
path = url.updateQueryString(path, 'jwt', auth.getAuthToken()) path = url.updateQueryString(path, 'jwt', this.$store.state.auth.token)
} }
return path return path
} }
......
...@@ -58,9 +58,9 @@ ...@@ -58,9 +58,9 @@
Keep your PRIVATE_TOKEN secret as it gives access to your account. Keep your PRIVATE_TOKEN secret as it gives access to your account.
</div> </div>
<pre> <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"> <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> </pre>
</div> </div>
</div> </div>
...@@ -83,7 +83,6 @@ curl -G -o "{{ track.files[0].filename }}" <template v-if="auth.user.authenticat ...@@ -83,7 +83,6 @@ curl -G -o "{{ track.files[0].filename }}" <template v-if="auth.user.authenticat
<script> <script>
import backend from '@/audio/backend' import backend from '@/audio/backend'
import auth from '@/auth'
import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon' import TrackFavoriteIcon from '@/components/favorites/TrackFavoriteIcon'
import PlayButton from '@/components/audio/PlayButton' import PlayButton from '@/components/audio/PlayButton'
...@@ -102,7 +101,6 @@ export default { ...@@ -102,7 +101,6 @@ export default {
data () { data () {
return { return {
backend: backend, backend: backend,
auth: auth,
showDownloadModal: false showDownloadModal: false
} }
} }
......
...@@ -39,12 +39,11 @@ ...@@ -39,12 +39,11 @@
</template> </template>
<script> <script>
import auth from '@/auth'
export default { export default {
name: 'login', name: 'login',
props: { props: {
next: {type: String} next: {type: String, default: '/'}
}, },
data () { data () {
return { return {
...@@ -72,14 +71,17 @@ export default { ...@@ -72,14 +71,17 @@ export default {
} }
// We need to pass the component's this context // We need to pass the component's this context
// to properly make use of http in the auth service // to properly make use of http in the auth service
auth.login(this, credentials, {path: this.next}, function (response) { this.$store.dispatch('auth/login', {
// error callback credentials,
if (response.status === 400) { next: this.next,
self.error = 'invalid_credentials' onError: response => {
} else { if (response.status === 400) {
self.error = 'unknown_error' self.error = 'invalid_credentials'
} else {
self.error = 'unknown_error'
}
} }
}).then((response) => { }).then(e => {
self.isLoading = false self.isLoading = false
}) })
} }
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
<div class="ui vertical stripe segment"> <div class="ui vertical stripe segment">
<div class="ui small text container"> <div class="ui small text container">
<h2>Are you sure you want to log out?</h2> <h2>Are you sure you want to log out?</h2>
<p>You are currently logged in as {{ auth.user.username }}</p> <p>You are currently logged in as {{ $store.state.auth.username }}</p>
<button class="ui button" @click="logout">Yes, log me out!</button> <button class="ui button" @click="$store.dispatch('auth/logout')">Yes, log me out!</button>
</form> </form>
</div> </div>
</div> </div>
...@@ -12,23 +12,8 @@ ...@@ -12,23 +12,8 @@
</template> </template>
<script> <script>
import auth from '@/auth'
export default { export default {
name: 'logout', 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'})
}
}
} }
</script> </script>
......
...@@ -3,17 +3,17 @@ ...@@ -3,17 +3,17 @@
<div v-if="isLoading" class="ui vertical segment"> <div v-if="isLoading" class="ui vertical segment">
<div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div> <div :class="['ui', 'centered', 'active', 'inline', 'loader']"></div>
</div> </div>
<template v-if="profile"> <template v-if="$store.state.auth.profile">
<div :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']"> <div :class="['ui', 'head', 'vertical', 'center', 'aligned', 'stripe', 'segment']">
<h2 class="ui center aligned icon header"> <h2 class="ui center aligned icon header">
<i class="circular inverted user green icon"></i> <i class="circular inverted user green icon"></i>
<div class="content"> <div class="content">
{{ profile.username }} {{ $store.state.auth.profile.username }}
<div class="sub header">Registered since {{ signupDate }}</div> <div class="sub header">Registered since {{ signupDate }}</div>
</div> </div>
</h2> </h2>
<div class="ui basic green label">this is you!</div> <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> <i class="star icon"></i>
Staff member Staff member
</div> </div>
...@@ -23,35 +23,21 @@ ...@@ -23,35 +23,21 @@
</template> </template>
<script> <script>
import auth from '@/auth' const dateFormat = require('dateformat')
var dateFormat = require('dateformat')
export default { export default {
name: 'login', name: 'login',
props: ['username'], props: ['username'],
data () {
return {
profile: null
}
},
created () { created () {
this.fetchProfile() this.$store.dispatch('auth/fetchProfile')
},
methods: {
fetchProfile () {
let self = this
auth.fetchProfile().then(data => {
self.profile = data
})
}
}, },
computed: { computed: {
signupDate () { signupDate () {
let d = new Date(this.profile.date_joined) let d = new Date(this.$store.state.auth.profile.date_joined)
return dateFormat(d, 'longDate') return dateFormat(d, 'longDate')
}, },
isLoading () { isLoading () {
return !this.profile return !this.$store.state.auth.profile
} }
} }
} }
......
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
<router-link class="ui item" to="/library" exact>Browse</router-link> <router-link class="ui item" to="/library" exact>Browse</router-link>
<router-link class="ui item" to="/library/artists" exact>Artists</router-link> <router-link class="ui item" to="/library/artists" exact>Artists</router-link>
<div class="ui secondary right menu"> <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="$store.state.auth.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/batches">Import batches</router-link>
</div> </div>
</div> </div>
<router-view :key="$route.fullPath"></router-view> <router-view :key="$route.fullPath"></router-view>
...@@ -14,15 +14,8 @@ ...@@ -14,15 +14,8 @@
<script> <script>
import auth from '@/auth'
export default { export default {
name: 'library', name: 'library'
data: function () {
return {
auth
}
}
} }
</script> </script>
......
...@@ -61,7 +61,6 @@ ...@@ -61,7 +61,6 @@
<script> <script>
import auth from '@/auth'
import url from '@/utils/url' import url from '@/utils/url'
import logger from '@/logging' import logger from '@/logging'
import backend from '@/audio/backend' import backend from '@/audio/backend'
...@@ -124,8 +123,8 @@ export default { ...@@ -124,8 +123,8 @@ export default {
downloadUrl () { downloadUrl () {
if (this.track.files.length > 0) { if (this.track.files.length > 0) {
let u = backend.absoluteUrl(this.track.files[0].path) let u = backend.absoluteUrl(this.track.files[0].path)
if (auth.user.authenticated) { if (this.$store.state.auth.authenticated) {
u = url.updateQueryString(u, 'jwt', auth.getAuthToken()) u = url.updateQueryString(u, 'jwt', this.$store.state.auth.token)
} }
return u return u
} }
......
...@@ -23,7 +23,6 @@ ...@@ -23,7 +23,6 @@
<script> <script>
import jQuery from 'jquery' import jQuery from 'jquery'
import config from '@/config' import config from '@/config'
import auth from '@/auth'
export default { export default {
props: { props: {
...@@ -66,7 +65,7 @@ export default { ...@@ -66,7 +65,7 @@ export default {
}, },
apiSettings: { apiSettings: {
beforeXHR: function (xhrObject, s) { beforeXHR: function (xhrObject, s) {
xhrObject.setRequestHeader('Authorization', auth.getAuthHeader()) xhrObject.setRequestHeader('Authorization', this.$store.getters['auth/header'])
return xhrObject return xhrObject
}, },
onResponse: function (initialResponse) { onResponse: function (initialResponse) {
......
...@@ -9,7 +9,6 @@ import Vue from 'vue' ...@@ -9,7 +9,6 @@ import Vue from 'vue'
import App from './App' import App from './App'
import router from './router' import router from './router'
import VueResource from 'vue-resource' import VueResource from 'vue-resource'
import auth from './auth'
import VueLazyload from 'vue-lazyload' import VueLazyload from 'vue-lazyload'
import store from './store' import store from './store'
...@@ -26,8 +25,8 @@ Vue.config.productionTip = false ...@@ -26,8 +25,8 @@ Vue.config.productionTip = false
Vue.http.interceptors.push(function (request, next) { Vue.http.interceptors.push(function (request, next) {
// modify headers // modify headers
if (auth.user.authenticated) { if (store.state.auth.authenticated) {
request.headers.set('Authorization', auth.getAuthHeader()) request.headers.set('Authorization', store.getters['auth/header'])
} }
next(function (response) { next(function (response) {
// redirect to login form when we get unauthorized response from server // redirect to login form when we get unauthorized response from server
...@@ -38,7 +37,7 @@ Vue.http.interceptors.push(function (request, next) { ...@@ -38,7 +37,7 @@ Vue.http.interceptors.push(function (request, next) {
}) })
}) })
auth.checkAuth() store.dispatch('auth/check')
/* eslint-disable no-new */ /* eslint-disable no-new */
new Vue({ new Vue({
el: '#app', 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 Vue from 'vue'
import Vuex from 'vuex' import Vuex from 'vuex'
import auth from './auth'
import queue from './queue' import queue from './queue'
import radios from './radios' import radios from './radios'
import player from './player' import player from './player'
...@@ -9,6 +10,7 @@ Vue.use(Vuex) ...@@ -9,6 +10,7 @@ Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
modules: { modules: {
auth,
queue, queue,
radios, radios,
player player
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment