diff --git a/assets/src/App.vue b/assets/src/App.vue
index 43e89b0c..8baf0567 100644
--- a/assets/src/App.vue
+++ b/assets/src/App.vue
@@ -6,6 +6,7 @@
 export default {
   name: 'app',
   mounted: function () {
+    // Remove loading animation.
     let loading = document.getElementById('loading')
     loading.classList.add('done')
 
diff --git a/assets/src/components/Files.vue b/assets/src/components/Files.vue
new file mode 100644
index 00000000..317b0a8b
--- /dev/null
+++ b/assets/src/components/Files.vue
@@ -0,0 +1,171 @@
+<template>
+  <div v-if="error">
+    <h2 class="message" v-if="error === 404">
+      <i class="material-icons">gps_off</i>
+      <span>This location can't be reached.</span>
+    </h2>
+    <h2 class="message" v-else-if="error === 403">
+      <i class="material-icons">error</i>
+      <span>You're not welcome here.</span>
+    </h2>
+    <h2 class="message" v-else>
+      <i class="material-icons">error_outline</i>
+      <span>Something really went wrong.</span>
+    </h2>
+  </div>
+  <editor v-else-if="isEditor"></editor>
+  <listing :class="{ multiple }" v-else-if="isListing"></listing>
+  <preview v-else-if="isPreview"></preview>
+</template>
+
+<script>
+import Preview from './Preview'
+import Listing from './Listing'
+import Editor from './Editor'
+import css from '@/utils/css'
+import api from '@/utils/api'
+import { mapGetters, mapState, mapMutations } from 'vuex'
+
+function updateColumnSizes () {
+  let columns = Math.floor(document.querySelector('main').offsetWidth / 300)
+  let items = css(['#listing.mosaic .item', '.mosaic#listing .item'])
+
+  if (columns === 0) columns = 1
+
+  items.style.width = `calc(${100 / columns}% - 1em)`
+}
+
+export default {
+  name: 'files',
+  components: {
+    Preview,
+    Listing,
+    Editor
+  },
+  computed: {
+    ...mapGetters([
+      'selectedCount'
+    ]),
+    ...mapState([
+      'req',
+      'user',
+      'reload',
+      'multiple',
+      'loading'
+    ]),
+    isListing () {
+      return this.req.kind === 'listing' && !this.loading
+    },
+    isPreview () {
+      return this.req.kind === 'preview' && !this.loading
+    },
+    isEditor () {
+      return this.req.kind === 'editor' && !this.loading
+    }
+  },
+  data: function () {
+    return {
+      error: null
+    }
+  },
+  created () {
+    this.fetchData()
+    console.log('created')
+  },
+  watch: {
+    '$route': 'fetchData',
+    'reload': function () {
+      this.$store.commit('setReload', false)
+      this.fetchData()
+    }
+  },
+  mounted () {
+    updateColumnSizes()
+    window.addEventListener('resize', updateColumnSizes)
+    window.addEventListener('keydown', this.keyEvent)
+  },
+  methods: {
+    ...mapMutations([ 'setLoading' ]),
+    fetchData () {
+      // Set loading to true and reset the error.
+      this.setLoading(true)
+      this.error = null
+
+      let url = this.$route.path
+      if (url === '') url = '/'
+      if (url[0] !== '/') url = '/' + url
+
+      api.fetch(url)
+      .then((trueURL) => {
+        if (!url.endsWith('/') && trueURL.endsWith('/')) {
+          console.log(trueURL)
+          window.history.replaceState(window.history.state, document.title, window.location.pathname + '/')
+        }
+
+        this.setLoading(false)
+      })
+      .catch(error => {
+        console.log(error)
+        this.error = error
+        this.setLoading(false)
+      })
+    },
+    keyEvent (event) {
+      // Esc!
+      if (event.keyCode === 27) {
+        this.$store.commit('closeHovers')
+
+        if (this.req.kind !== 'listing') {
+          return
+        }
+
+        // If we're on a listing, unselect all files and folders.
+        let items = document.getElementsByClassName('item')
+        Array.from(items).forEach(link => {
+          link.setAttribute('aria-selected', false)
+        })
+
+        this.$store.commit('resetSelected')
+      }
+
+      // Del!
+      if (event.keyCode === 46) {
+        if (this.showDeleteButton && this.req.kind !== 'editor') {
+          this.$store.commit('showHover', 'delete')
+        }
+      }
+
+      // F1!
+      if (event.keyCode === 112) {
+        event.preventDefault()
+        this.$store.commit('showHover', 'help')
+      }
+
+      // F2!
+      if (event.keyCode === 113) {
+        if (this.showRenameButton) {
+          this.$store.commit('showHover', 'rename')
+        }
+      }
+
+      // CTRL + S
+      if (event.ctrlKey || event.metaKey) {
+        if (String.fromCharCode(event.which).toLowerCase() === 's') {
+          event.preventDefault()
+
+          if (this.req.kind !== 'editor') {
+            document.getElementById('download-button').click()
+            return
+          }
+        }
+      }
+    },
+    openSidebar () {
+      this.$store.commit('showHover', 'sidebar')
+    },
+    openSearch () {
+      this.$store.commit('showHover', 'search')
+    }
+  }
+}
+</script>
diff --git a/assets/src/components/Header.vue b/assets/src/components/Header.vue
new file mode 100644
index 00000000..efd8f3a0
--- /dev/null
+++ b/assets/src/components/Header.vue
@@ -0,0 +1,135 @@
+<template>
+  <header>
+    <div>
+      <button @click="openSidebar" aria-label="Toggle sidebar" title="Toggle sidebar" class="action">
+        <i class="material-icons">menu</i>
+      </button>
+      <img src="../assets/logo.svg" alt="File Manager">
+      <search></search>
+    </div>
+    <div>
+      <button @click="openSearch" aria-label="Search" title="Search" class="search-button action">
+        <i class="material-icons">search</i>
+      </button>
+
+      <button v-show="showSaveButton" aria-label="Save" class="action" id="save-button">
+        <i class="material-icons" title="Save">save</i>
+      </button>
+      <rename-button v-show="showRenameButton"></rename-button>
+      <move-button v-show="showMoveButton"></move-button>
+      <delete-button v-show="showDeleteButton"></delete-button>
+      <switch-button v-show="showSwitchButton"></switch-button>
+      <download-button v-show="showCommonButton"></download-button>
+      <upload-button v-show="showUpload"></upload-button>
+      <info-button v-show="showCommonButton"></info-button>
+
+      <button v-show="showSelectButton" @click="$store.commit('multiple', true)" aria-label="Select multiple" class="action">
+        <i class="material-icons">check_circle</i>
+        <span>Select</span>
+      </button>
+    </div>
+  </header>
+</template>
+
+<script>
+import Search from './Search'
+import InfoButton from './buttons/Info'
+import DeleteButton from './buttons/Delete'
+import RenameButton from './buttons/Rename'
+import UploadButton from './buttons/Upload'
+import DownloadButton from './buttons/Download'
+import SwitchButton from './buttons/SwitchView'
+import MoveButton from './buttons/Move'
+import {mapGetters, mapState} from 'vuex'
+
+export default {
+  name: 'main',
+  components: {
+    Search,
+    InfoButton,
+    DeleteButton,
+    RenameButton,
+    DownloadButton,
+    UploadButton,
+    SwitchButton,
+    MoveButton
+  },
+  computed: {
+    ...mapGetters([
+      'selectedCount'
+    ]),
+    ...mapState([
+      'req',
+      'user',
+      'loading',
+      'reload',
+      'multiple'
+    ]),
+    showSelectButton () {
+      return this.req.kind === 'listing' && !this.loading && this.$route.name === 'Files'
+    },
+    showSaveButton () {
+      return (this.req.kind === 'editor' && !this.loading) || this.$route.name === 'User'
+    },
+    showSwitchButton () {
+      return this.req.kind === 'listing' && this.$route.name === 'Files' && !this.loading
+    },
+    showCommonButton () {
+      return !(this.$route.name !== 'Files' || this.loading)
+    },
+    showUpload () {
+      if (this.$route.name !== 'Files' || this.loading) return false
+
+      if (this.req.kind === 'editor') return false
+      return this.user.allowNew
+    },
+    showDeleteButton () {
+      if (this.$route.name !== 'Files' || this.loading) return false
+
+      if (this.req.kind === 'listing') {
+        if (this.selectedCount === 0) {
+          return false
+        }
+
+        return this.user.allowEdit
+      }
+
+      return this.user.allowEdit
+    },
+    showRenameButton () {
+      if (this.$route.name !== 'Files' || this.loading) return false
+
+      if (this.req.kind === 'listing') {
+        if (this.selectedCount === 1) {
+          return this.user.allowEdit
+        }
+
+        return false
+      }
+
+      return this.user.allowEdit
+    },
+    showMoveButton () {
+      if (this.$route.name !== 'Files' || this.loading) return false
+
+      if (this.req.kind !== 'listing') {
+        return false
+      }
+
+      if (this.selectedCount > 0) {
+        return this.user.allowEdit
+      }
+
+      return false
+    }
+  },
+  methods: {
+    openSidebar () {
+      this.$store.commit('showHover', 'sidebar')
+    },
+    openSearch () {
+      this.$store.commit('showHover', 'search')
+    }
+  }
+}
+</script>
diff --git a/assets/src/components/Main.vue b/assets/src/components/Main.vue
index fda49a1c..de9b0f87 100644
--- a/assets/src/components/Main.vue
+++ b/assets/src/components/Main.vue
@@ -1,273 +1,34 @@
 <template>
-  <div :class="{ multiple, loading }">
-    <header>
-      <div>
-        <button @click="openSidebar" aria-label="Toggle sidebar" title="Toggle sidebar" class="action">
-          <i class="material-icons">menu</i>
-        </button>
-        <img src="../assets/logo.svg" alt="File Manager">
-        <search></search>
-      </div>
-      <div>
-        <button @click="openSearch" aria-label="Search" title="Search" class="search-button action">
-          <i class="material-icons">search</i>
-        </button>
-
-        <button v-show="isEditor" aria-label="Save" class="action" id="save-button">
-          <i class="material-icons" title="Save">save</i>
-        </button>
-        <rename-button v-show="!loading && showRenameButton"></rename-button>
-        <move-button v-show="!loading && showMoveButton"></move-button>
-        <delete-button v-show="!loading && showDeleteButton"></delete-button>
-        <switch-button v-show="!loading && req.kind !== 'editor'"></switch-button>
-        <download-button></download-button>
-        <upload-button v-show="!loading && showUpload"></upload-button>
-        <info-button></info-button>
-
-        <button v-show="isListing" @click="$store.commit('multiple', true)" aria-label="Select multiple" class="action">
-          <i class="material-icons">check_circle</i>
-          <span>Select</span>
-        </button>
-      </div>
-    </header>
-
+  <div>
+    <site-header></site-header>
     <sidebar></sidebar>
-
     <main>
-      <div v-if="loading">
-        <h2 class="message">
-          <span>Loading...</span>
-        </h2>
-      </div>
-      <div v-else-if="error">
-        <h2 class="message" v-if="error === 404">
-          <i class="material-icons">gps_off</i>
-          <span>This location can't be reached.</span>
-        </h2>
-        <h2 class="message" v-else-if="error === 403">
-          <i class="material-icons">error</i>
-          <span>You're not welcome here.</span>
-        </h2>
-        <h2 class="message" v-else>
-          <i class="material-icons">error_outline</i>
-          <span>Something really went wrong.</span>
-        </h2>
-      </div>
-      <editor v-else-if="isEditor"></editor>
-      <listing v-else-if="isListing"></listing>
-      <preview v-else-if="isPreview"></preview>
+      <router-view></router-view>
     </main>
-
     <prompts></prompts>
   </div>
 </template>
 
 <script>
 import Search from './Search'
-import Preview from './Preview'
-import Listing from './Listing'
-import Editor from './Editor'
 import Sidebar from './Sidebar'
 import Prompts from './prompts/Prompts'
-import InfoButton from './buttons/Info'
-import DeleteButton from './buttons/Delete'
-import RenameButton from './buttons/Rename'
-import UploadButton from './buttons/Upload'
-import DownloadButton from './buttons/Download'
-import SwitchButton from './buttons/SwitchView'
-import MoveButton from './buttons/Move'
-import css from '@/utils/css'
-import api from '@/utils/api'
-import {mapGetters, mapState} from 'vuex'
-
-function updateColumnSizes () {
-  let columns = Math.floor(document.querySelector('main').offsetWidth / 300)
-  let items = css(['#listing.mosaic .item', '.mosaic#listing .item'])
-
-  if (columns === 0) columns = 1
-
-  items.style.width = `calc(${100 / columns}% - 1em)`
-}
+import SiteHeader from './Header'
 
 export default {
   name: 'main',
   components: {
     Search,
-    Preview,
-    Listing,
-    Editor,
     Sidebar,
-    InfoButton,
-    DeleteButton,
-    RenameButton,
-    DownloadButton,
-    UploadButton,
-    SwitchButton,
-    MoveButton,
+    SiteHeader,
     Prompts
   },
-  computed: {
-    ...mapGetters([
-      'selectedCount'
-    ]),
-    ...mapState([
-      'req',
-      'user',
-      'reload',
-      'multiple'
-    ]),
-    isListing () {
-      return this.req.kind === 'listing' && !this.loading
-    },
-    isPreview () {
-      return this.req.kind === 'preview' && !this.loading
-    },
-    isEditor () {
-      return this.req.kind === 'editor' && !this.loading
-    },
-    showUpload () {
-      if (this.req.kind === 'editor') return false
-      return this.user.allowNew
-    },
-    showDeleteButton () {
-      if (this.req.kind === 'listing') {
-        if (this.selectedCount === 0) {
-          return false
-        }
-
-        return this.user.allowEdit
-      }
-
-      return this.user.allowEdit
-    },
-    showRenameButton () {
-      if (this.req.kind === 'listing') {
-        if (this.selectedCount === 1) {
-          return this.user.allowEdit
-        }
-
-        return false
-      }
-
-      return this.user.allowEdit
-    },
-    showMoveButton () {
-      if (this.req.kind !== 'listing') {
-        return false
-      }
-
-      if (this.selectedCount > 0) {
-        return this.user.allowEdit
-      }
-
-      return false
-    }
-  },
-  data: function () {
-    return {
-      loading: true,
-      error: null
-    }
-  },
-  created () {
-    this.fetchData()
-  },
   watch: {
-    '$route': 'fetchData',
-    'reload': function () {
-      this.$store.commit('setReload', false)
-      this.fetchData()
-    }
-  },
-  mounted () {
-    updateColumnSizes()
-    window.addEventListener('resize', updateColumnSizes)
-    window.addEventListener('keydown', this.keyEvent)
-  },
-  methods: {
-    fetchData () {
-      // Set loading to true and reset the error.
-      this.loading = true
-      this.error = null
-
+    '$route': function () {
       // Reset selected items and multiple selection.
       this.$store.commit('resetSelected')
       this.$store.commit('multiple', false)
       this.$store.commit('closeHovers')
-
-      let url = this.$route.path
-      if (url === '') url = '/'
-      if (url[0] !== '/') url = '/' + url
-
-      api.fetch(url)
-      .then((trueURL) => {
-        if (!url.endsWith('/') && trueURL.endsWith('/')) {
-          window.history.replaceState(window.history.state, document.title, window.location.pathname + '/')
-        }
-
-        this.loading = false
-      })
-      .catch(error => {
-        console.log(error)
-        this.error = error
-        this.loading = false
-      })
-    },
-    keyEvent (event) {
-      // Esc!
-      if (event.keyCode === 27) {
-        this.$store.commit('closeHovers')
-
-        if (this.req.kind !== 'listing') {
-          return
-        }
-
-        // If we're on a listing, unselect all files and folders.
-        let items = document.getElementsByClassName('item')
-        Array.from(items).forEach(link => {
-          link.setAttribute('aria-selected', false)
-        })
-
-        this.$store.commit('resetSelected')
-      }
-
-      // Del!
-      if (event.keyCode === 46) {
-        if (this.showDeleteButton && this.req.kind !== 'editor') {
-          this.$store.commit('showHover', 'delete')
-        }
-      }
-
-      // F1!
-      if (event.keyCode === 112) {
-        event.preventDefault()
-        this.$store.commit('showHover', 'help')
-      }
-
-      // F2!
-      if (event.keyCode === 113) {
-        if (this.showRenameButton) {
-          this.$store.commit('showHover', 'rename')
-        }
-      }
-
-      // CTRL + S
-      if (event.ctrlKey || event.metaKey) {
-        if (String.fromCharCode(event.which).toLowerCase() === 's') {
-          event.preventDefault()
-
-          if (this.req.kind !== 'editor') {
-            document.getElementById('download-button').click()
-            return
-          }
-        }
-      }
-    },
-    openSidebar () {
-      this.$store.commit('showHover', 'sidebar')
-    },
-    openSearch () {
-      this.$store.commit('showHover', 'search')
     }
   }
 }
diff --git a/assets/src/components/Settings.vue b/assets/src/components/Settings.vue
new file mode 100644
index 00000000..1229ddab
--- /dev/null
+++ b/assets/src/components/Settings.vue
@@ -0,0 +1,11 @@
+<template>
+  <div>
+    <h1>Settings</h1>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'settings'
+}
+</script>
diff --git a/assets/src/components/Sidebar.vue b/assets/src/components/Sidebar.vue
index fc5457a9..8f2d8adb 100644
--- a/assets/src/components/Sidebar.vue
+++ b/assets/src/components/Sidebar.vue
@@ -25,7 +25,7 @@
     </div>
 
     <div>
-      <router-link class="action" to="/dashboard" aria-label="Settings" title="Settings">
+      <router-link class="action" to="/settings" aria-label="Settings" title="Settings">
         <i class="material-icons">settings_applications</i>
         <span>Settings</span>
       </router-link>
diff --git a/assets/src/components/User.vue b/assets/src/components/User.vue
new file mode 100644
index 00000000..43b971e6
--- /dev/null
+++ b/assets/src/components/User.vue
@@ -0,0 +1,177 @@
+<template>
+  <div class="dashboard">
+    <h1>User</h1>
+
+    <p><label for="username">Username</label><input type="text" v-model="username" name="username"></p>
+    <p><label for="password">Password</label><input type="password" :disabled="passwordBlock" v-model="password" name="password"></p>
+    <p><label for="scope">Scope</label><input type="text" v-model="scope" name="scope"></p>
+
+    <hr>
+
+    <h2>Permissions</h2>
+
+    <p class="small">You can set the user to be an administrator or choose the permissions individually.
+      If you select "Administrator", all of the other options will be automatically checked.
+      The management of users remains a privilege of an administrator.</p>
+
+    <p><input type="checkbox" v-model="admin"> Administrator</p>
+    <p><input type="checkbox" :disabled="admin" v-model="allowNew"> Create new files and directories</p>
+    <p><input type="checkbox" :disabled="admin" v-model="allowEdit"> Edit, rename and delete files or directories.</p>
+    <p><input type="checkbox" :disabled="admin" v-model="allowCommands"> Execute commands</p>
+
+    <h3>Commands</h3>
+
+    <p class="small">A space separated list with the available commands for this user. Example: <i>git svn hg</i>.</p>
+
+    <input type="text" v-model="commands">
+
+    <hr>
+
+    <h2>Rules</h2>
+
+    <p class="small">Here you can define a set of allow and disallow rules for this specific user. The blocked files won't
+      show up in the listings and they won't be accessible to the user. We support regex and paths relative to
+      the user's scope.</p>
+
+    <p class="small">Each rule goes in one different line and must start with the keyword <code>allow</code> or <code>disallow</code>.
+      Then you should write <code>regex</code> if you are using a regular expression and then the expression or the path.</p>
+
+    <p class="small"><strong>Examples</strong></p>
+
+    <ul class="small">
+      <li><code>disallow regex \\/\\..+</code> - prevents the access to any dot file (such as .git, .gitignore) in every folder.</li>
+      <li><code>disallow /Caddyfile</code> - blocks the access to the file named <i>Caddyfile</i> on the root of the scope</li>
+    </ul>
+
+    <textarea v-model="rules"></textarea>
+
+    <hr>
+
+    <h2>CSS</h2>
+
+    <p class="small">Costum user CSS</p>
+
+    <textarea name="css"></textarea>
+  </div>
+</template>
+
+<script>
+import api from '@/utils/api'
+
+export default {
+  name: 'user',
+  data: () => {
+    return {
+      admin: false,
+      allowNew: false,
+      allowEdit: false,
+      allowCommands: false,
+      passwordBlock: true,
+      password: '',
+      username: '',
+      scope: '',
+      rules: '',
+      css: '',
+      commands: ''
+    }
+  },
+  created () {
+    if (this.$route.path === '/users/new') return
+
+    api.getUser(this.$route.params[0]).then(user => {
+      this.admin = user.admin
+      this.allowCommands = user.allowCommands
+      this.allowNew = user.allowNew
+      this.allowEdit = user.allowEdit
+      this.scope = user.filesystem
+      this.username = user.username
+      this.commands = user.commands.join(' ')
+      this.css = user.css
+
+      for (let rule of user.rules) {
+        if (rule.allow) {
+          this.rules += 'allow '
+        } else {
+          this.rules += 'disallow '
+        }
+
+        if (rule.regex) {
+          this.rules += 'regex ' + rule.regexp.raw
+        } else {
+          this.rules += rule.path
+        }
+
+        this.rules += '\n'
+      }
+    }).catch(error => {
+      console.log(error)
+    })
+  },
+  watch: {
+    admin: function () {
+      if (!this.admin) return
+      this.allowCommands = true
+      this.allowEdit = true
+      this.allowNew = true
+    }
+  }
+}
+</script>
+
+<style>
+.dashboard {
+  max-width: 600px;
+}
+
+.dashboard textarea,
+.dashboard input[type="text"],
+.dashboard input[type="password"] {
+  padding: .5em 1em;
+  display: block;
+  border: 1px solid #e9e9e9;
+  transition: .2s ease border;
+  color: #333;
+  width: 100%;
+}
+
+.dashboard textarea:focus,
+.dashboard textarea:hover,
+.dashboard input[type="text"]:focus,
+.dashboard input[type="password"]:focus,
+.dashboard input[type="text"]:hover,
+.dashboard input[type="password"]:hover {
+  border-color: #9f9f9f;
+}
+
+.dashboard textarea {
+  font-family: monospace;
+  min-height: 10em;
+  resize: vertical;
+}
+
+.dashboard p label {
+  margin-bottom: .2em;
+  display: block;
+  font-size: .8em
+}
+
+hr {
+    border-bottom: 2px solid rgba(181, 181, 181, 0.5);
+    border-top: 0;
+    border-right: 0;
+    border-left: 0;
+    margin: 1em 0;
+}
+
+li code,
+p code {
+  background: rgba(0, 0, 0, 0.05);
+  padding: .1em;
+  border-radius: .2em;
+}
+
+.small {
+  font-size: .8em;
+  line-height: 1.5;
+}
+</style>
diff --git a/assets/src/components/Users.vue b/assets/src/components/Users.vue
new file mode 100644
index 00000000..b6c01803
--- /dev/null
+++ b/assets/src/components/Users.vue
@@ -0,0 +1,11 @@
+<template>
+  <div>
+    <h1>Users</h1>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'users'
+}
+</script>
diff --git a/assets/src/css/base.css b/assets/src/css/base.css
index 3d9e6624..898ece28 100644
--- a/assets/src/css/base.css
+++ b/assets/src/css/base.css
@@ -3,6 +3,7 @@ body {
   padding-top: 4em;
   background-color: #f8f8f8;
   user-select: none;
+  color: #333;
 }
 
 * {
diff --git a/assets/src/router/index.js b/assets/src/router/index.js
index 5695a74e..16a6b01d 100644
--- a/assets/src/router/index.js
+++ b/assets/src/router/index.js
@@ -2,6 +2,10 @@ import Vue from 'vue'
 import Router from 'vue-router'
 import Login from '@/components/Login'
 import Main from '@/components/Main'
+import Files from '@/components/Files'
+import Users from '@/components/Users'
+import User from '@/components/User'
+import Settings from '@/components/Settings'
 import auth from '@/utils/auth.js'
 
 Vue.use(Router)
@@ -40,11 +44,29 @@ const router = new Router({
       children: [
         {
           path: '/files/*',
-          name: 'Files'
+          name: 'Files',
+          component: Files
         },
         {
-          path: '/dashboard',
-          name: 'Dashboard'
+          path: '/settings',
+          name: 'Settings',
+          component: Settings
+        },
+        {
+          path: '/users',
+          name: 'Users',
+          component: Users
+        },
+        {
+          path: '/users/',
+          redirect: {
+            path: '/users'
+          }
+        },
+        {
+          path: '/users/*',
+          name: 'User',
+          component: User
         },
         {
           path: '/*',
diff --git a/assets/src/store/index.js b/assets/src/store/index.js
index 43a79789..825237d8 100644
--- a/assets/src/store/index.js
+++ b/assets/src/store/index.js
@@ -10,6 +10,7 @@ const state = {
   req: {},
   baseURL: document.querySelector('meta[name="base"]').getAttribute('content'),
   jwt: '',
+  loading: false,
   reload: false,
   selected: [],
   multiple: false,
diff --git a/assets/src/store/mutations.js b/assets/src/store/mutations.js
index 42581bf7..4035439a 100644
--- a/assets/src/store/mutations.js
+++ b/assets/src/store/mutations.js
@@ -16,6 +16,7 @@ const mutations = {
     state.show = 'error'
     state.showMessage = value
   },
+  setLoading: (state, value) => { state.loading = value },
   setReload: (state, value) => { state.reload = value },
   setUser: (state, value) => (state.user = value),
   setJWT: (state, value) => (state.jwt = value),
diff --git a/assets/src/utils/api.js b/assets/src/utils/api.js
index b20c67b5..d76f1397 100644
--- a/assets/src/utils/api.js
+++ b/assets/src/utils/api.js
@@ -105,7 +105,7 @@ function move (oldLink, newLink) {
 
   return new Promise((resolve, reject) => {
     let request = new window.XMLHttpRequest()
-    request.open('POST', `${store.state.baseURL}/api/resource${oldLink}`, true)
+    request.open('PATCH', `${store.state.baseURL}/api/resource${oldLink}`, true)
     request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
     request.setRequestHeader('Destination', newLink)
 
@@ -190,6 +190,27 @@ function download (format, ...files) {
   window.open(url)
 }
 
+function getUser (id) {
+  return new Promise((resolve, reject) => {
+    let request = new window.XMLHttpRequest()
+    request.open('GET', `${store.state.baseURL}/api/users/${id}`, true)
+    request.setRequestHeader('Authorization', `Bearer ${store.state.jwt}`)
+
+    request.onload = () => {
+      switch (request.status) {
+        case 200:
+          resolve(JSON.parse(request.responseText))
+          break
+        default:
+          reject(request.responseText)
+          break
+      }
+    }
+    request.onerror = (error) => reject(error)
+    request.send()
+  })
+}
+
 export default {
   delete: rm,
   fetch,
@@ -199,5 +220,6 @@ export default {
   post,
   command,
   search,
-  download
+  download,
+  getUser
 }
diff --git a/filemanager.go b/filemanager.go
index b4f75ace..1aee0f76 100644
--- a/filemanager.go
+++ b/filemanager.go
@@ -85,21 +85,21 @@ type User struct {
 // Rule is a dissalow/allow rule.
 type Rule struct {
 	// Regex indicates if this rule uses Regular Expressions or not.
-	Regex bool
+	Regex bool `json:"regex"`
 
 	// Allow indicates if this is an allow rule. Set 'false' to be a disallow rule.
-	Allow bool
+	Allow bool `json:"allow"`
 
 	// Path is the corresponding URL path for this rule.
-	Path string
+	Path string `json:"path"`
 
 	// Regexp is the regular expression. Only use this when 'Regex' was set to true.
-	Regexp *Regexp
+	Regexp *Regexp `json:"regexp"`
 }
 
 // Regexp is a regular expression wrapper around native regexp.
 type Regexp struct {
-	Raw    string
+	Raw    string `json:"raw"`
 	regexp *regexp.Regexp
 }