Now use vuex to manage state for authentication
This commit is contained in:
parent
df94ae37bf
commit
b5ce65fc3e
|
@ -1,99 +0,0 @@
|
|||
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',
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
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')
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue