PersonalAccessTokens.vue 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <style scoped>
  2. .action-link {
  3. cursor: pointer;
  4. }
  5. .m-b-none {
  6. margin-bottom: 0;
  7. }
  8. </style>
  9. <template>
  10. <div>
  11. <div>
  12. <div class="panel panel-default">
  13. <div class="panel-heading">
  14. <div style="display: flex; justify-content: space-between; align-items: center;">
  15. <span>
  16. Personal Access Tokens
  17. </span>
  18. <a class="action-link" @click="showCreateTokenForm">
  19. Create New Token
  20. </a>
  21. </div>
  22. </div>
  23. <div class="panel-body">
  24. <!-- No Tokens Notice -->
  25. <p class="m-b-none" v-if="tokens.length === 0">
  26. You have not created any personal access tokens.
  27. </p>
  28. <!-- Personal Access Tokens -->
  29. <table class="table table-borderless m-b-none" v-if="tokens.length > 0">
  30. <thead>
  31. <tr>
  32. <th>Name</th>
  33. <th></th>
  34. </tr>
  35. </thead>
  36. <tbody>
  37. <tr v-for="token in tokens">
  38. <!-- Client Name -->
  39. <td style="vertical-align: middle;">
  40. {{ token.name }}
  41. </td>
  42. <!-- Delete Button -->
  43. <td style="vertical-align: middle;">
  44. <a class="action-link text-danger" @click="revoke(token)">
  45. Delete
  46. </a>
  47. </td>
  48. </tr>
  49. </tbody>
  50. </table>
  51. </div>
  52. </div>
  53. </div>
  54. <!-- Create Token Modal -->
  55. <div class="modal fade" id="modal-create-token" tabindex="-1" role="dialog">
  56. <div class="modal-dialog">
  57. <div class="modal-content">
  58. <div class="modal-header">
  59. <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
  60. <h4 class="modal-title">
  61. Create Token
  62. </h4>
  63. </div>
  64. <div class="modal-body">
  65. <!-- Form Errors -->
  66. <div class="alert alert-danger" v-if="form.errors.length > 0">
  67. <p><strong>Whoops!</strong> Something went wrong!</p>
  68. <br>
  69. <ul>
  70. <li v-for="error in form.errors">
  71. {{ error }}
  72. </li>
  73. </ul>
  74. </div>
  75. <!-- Create Token Form -->
  76. <form class="form-horizontal" role="form" @submit.prevent="store">
  77. <!-- Name -->
  78. <div class="form-group">
  79. <label class="col-md-4 control-label">Name</label>
  80. <div class="col-md-6">
  81. <input id="create-token-name" type="text" class="form-control" name="name" v-model="form.name">
  82. </div>
  83. </div>
  84. <!-- Scopes -->
  85. <div class="form-group" v-if="scopes.length > 0">
  86. <label class="col-md-4 control-label">Scopes</label>
  87. <div class="col-md-6">
  88. <div v-for="scope in scopes">
  89. <div class="checkbox">
  90. <label>
  91. <input type="checkbox"
  92. @click="toggleScope(scope.id)"
  93. :checked="scopeIsAssigned(scope.id)">
  94. {{ scope.id }}
  95. </label>
  96. </div>
  97. </div>
  98. </div>
  99. </div>
  100. </form>
  101. </div>
  102. <!-- Modal Actions -->
  103. <div class="modal-footer">
  104. <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
  105. <button type="button" class="btn btn-primary" @click="store">
  106. Create
  107. </button>
  108. </div>
  109. </div>
  110. </div>
  111. </div>
  112. <!-- Access Token Modal -->
  113. <div class="modal fade" id="modal-access-token" tabindex="-1" role="dialog">
  114. <div class="modal-dialog">
  115. <div class="modal-content">
  116. <div class="modal-header">
  117. <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
  118. <h4 class="modal-title">
  119. Personal Access Token
  120. </h4>
  121. </div>
  122. <div class="modal-body">
  123. <p>
  124. Here is your new personal access token. This is the only time it will be shown so don't lose it!
  125. You may now use this token to make API requests.
  126. </p>
  127. <pre><code>{{ accessToken }}</code></pre>
  128. </div>
  129. <!-- Modal Actions -->
  130. <div class="modal-footer">
  131. <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
  132. </div>
  133. </div>
  134. </div>
  135. </div>
  136. </div>
  137. </template>
  138. <script>
  139. export default {
  140. /*
  141. * The component's data.
  142. */
  143. data() {
  144. return {
  145. accessToken: null,
  146. tokens: [],
  147. scopes: [],
  148. form: {
  149. name: '',
  150. scopes: [],
  151. errors: []
  152. }
  153. };
  154. },
  155. /**
  156. * Prepare the component (Vue 1.x).
  157. */
  158. ready() {
  159. this.prepareComponent();
  160. },
  161. /**
  162. * Prepare the component (Vue 2.x).
  163. */
  164. mounted() {
  165. this.prepareComponent();
  166. },
  167. methods: {
  168. /**
  169. * Prepare the component.
  170. */
  171. prepareComponent() {
  172. this.getTokens();
  173. this.getScopes();
  174. $('#modal-create-token').on('shown.bs.modal', () => {
  175. $('#create-token-name').focus();
  176. });
  177. },
  178. /**
  179. * Get all of the personal access tokens for the user.
  180. */
  181. getTokens() {
  182. axios.get('/oauth/personal-access-tokens')
  183. .then(response => {
  184. this.tokens = response.data;
  185. });
  186. },
  187. /**
  188. * Get all of the available scopes.
  189. */
  190. getScopes() {
  191. axios.get('/oauth/scopes')
  192. .then(response => {
  193. this.scopes = response.data;
  194. });
  195. },
  196. /**
  197. * Show the form for creating new tokens.
  198. */
  199. showCreateTokenForm() {
  200. $('#modal-create-token').modal('show');
  201. },
  202. /**
  203. * Create a new personal access token.
  204. */
  205. store() {
  206. this.accessToken = null;
  207. this.form.errors = [];
  208. axios.post('/oauth/personal-access-tokens', this.form)
  209. .then(response => {
  210. this.form.name = '';
  211. this.form.scopes = [];
  212. this.form.errors = [];
  213. this.tokens.push(response.data.token);
  214. this.showAccessToken(response.data.accessToken);
  215. })
  216. .catch(error => {
  217. if (typeof error.response.data === 'object') {
  218. this.form.errors = _.flatten(_.toArray(error.response.data));
  219. } else {
  220. this.form.errors = ['Something went wrong. Please try again.'];
  221. }
  222. });
  223. },
  224. /**
  225. * Toggle the given scope in the list of assigned scopes.
  226. */
  227. toggleScope(scope) {
  228. if (this.scopeIsAssigned(scope)) {
  229. this.form.scopes = _.reject(this.form.scopes, s => s == scope);
  230. } else {
  231. this.form.scopes.push(scope);
  232. }
  233. },
  234. /**
  235. * Determine if the given scope has been assigned to the token.
  236. */
  237. scopeIsAssigned(scope) {
  238. return _.indexOf(this.form.scopes, scope) >= 0;
  239. },
  240. /**
  241. * Show the given access token to the user.
  242. */
  243. showAccessToken(accessToken) {
  244. $('#modal-create-token').modal('hide');
  245. this.accessToken = accessToken;
  246. $('#modal-access-token').modal('show');
  247. },
  248. /**
  249. * Revoke the given token.
  250. */
  251. revoke(token) {
  252. axios.delete('/oauth/personal-access-tokens/' + token.id)
  253. .then(response => {
  254. this.getTokens();
  255. });
  256. }
  257. }
  258. }
  259. </script>