diff --git a/platypush/backend/http/app/routes/auth.py b/platypush/backend/http/app/routes/auth.py
index 8563dc6810..7300feeea3 100644
--- a/platypush/backend/http/app/routes/auth.py
+++ b/platypush/backend/http/app/routes/auth.py
@@ -4,8 +4,10 @@ import logging
from flask import Blueprint, request, abort, jsonify
+from platypush.backend.http.app.utils import authenticate
from platypush.backend.http.app.utils.auth import (
UserAuthStatus,
+ current_user,
get_current_user_or_auth_status,
)
from platypush.exceptions.user import UserException
@@ -223,7 +225,9 @@ def _auth_get():
response = get_current_user_or_auth_status(request)
if isinstance(response, User):
user = response
- return jsonify({'status': 'ok', 'user_id': user.id, 'username': user.username})
+ return jsonify(
+ {'status': 'ok', 'user_id': user.user_id, 'username': user.username}
+ )
if response:
status = response
@@ -283,6 +287,67 @@ def _auth_delete():
return UserAuthStatus.INVALID_SESSION.to_response()
+def _tokens_get():
+ user = current_user()
+ if not user:
+ return UserAuthStatus.INVALID_CREDENTIALS.to_response()
+
+ tokens = UserManager().get_api_tokens(username=str(user.username))
+ return jsonify(
+ {
+ 'tokens': [
+ {
+ 'id': t.id,
+ 'name': t.name,
+ 'created_at': t.created_at,
+ 'expires_at': t.expires_at,
+ }
+ for t in tokens
+ ]
+ }
+ )
+
+
+def _tokens_delete():
+ args = {}
+
+ try:
+ payload = json.loads(request.get_data(as_text=True))
+ token = payload.get('token')
+ if token:
+ args['token'] = token
+ else:
+ token_id = payload.get('token_id')
+ if token_id:
+ args['token_id'] = token_id
+
+ assert args, 'No token or token_id specified'
+ except (AssertionError, json.JSONDecodeError):
+ return UserAuthStatus.INVALID_TOKEN.to_response()
+
+ user_manager = UserManager()
+ user = current_user()
+ if not user:
+ return UserAuthStatus.INVALID_CREDENTIALS.to_response()
+
+ args['username'] = str(user.username)
+
+ try:
+ user_manager.delete_api_token(**args)
+ return jsonify({'status': 'ok'})
+ except AssertionError as e:
+ return (
+ jsonify({'status': 'error', 'error': 'bad_request', 'message': str(e)}),
+ 400,
+ )
+ except UserException:
+ return UserAuthStatus.INVALID_CREDENTIALS.to_response()
+ except Exception as e:
+ log.error('Token deletion error', exc_info=e)
+
+ return UserAuthStatus.UNKNOWN_ERROR.to_response()
+
+
@auth.route('/auth', methods=['GET', 'POST', 'DELETE'])
def auth_endpoint():
"""
@@ -326,4 +391,22 @@ def auth_endpoint():
return UserAuthStatus.INVALID_METHOD.to_response()
+@auth.route('/tokens', methods=['GET', 'DELETE'])
+@authenticate()
+def tokens_route():
+ """
+ :return: The list of API tokens created by the logged in user.
+ Note that this endpoint is only accessible by authenticated users
+ and it won't return the clear-text token values, as those aren't
+ stored in the database anyway.
+ """
+ if request.method == 'GET':
+ return _tokens_get()
+
+ if request.method == 'DELETE':
+ return _tokens_delete()
+
+ return UserAuthStatus.INVALID_METHOD.to_response()
+
+
# vim:sw=4:ts=4:et:
diff --git a/platypush/backend/http/app/utils/auth/status.py b/platypush/backend/http/app/utils/auth/status.py
index fc3a3e0a6f..abf3beecfd 100644
--- a/platypush/backend/http/app/utils/auth/status.py
+++ b/platypush/backend/http/app/utils/auth/status.py
@@ -53,6 +53,9 @@ class UserAuthStatus(Enum):
REGISTRATION_REQUIRED = StatusValue(
412, AuthenticationStatus.REGISTRATION_REQUIRED, 'Please create a user first'
)
+ UNKNOWN_ERROR = StatusValue(
+ 500, AuthenticationStatus.UNKNOWN_ERROR, 'Unknown error'
+ )
def to_dict(self):
return {
diff --git a/platypush/backend/http/webapp/src/components/panels/Settings/Tokens/ApiToken.vue b/platypush/backend/http/webapp/src/components/panels/Settings/Tokens/ApiToken.vue
index c4920003d0..0f27303312 100644
--- a/platypush/backend/http/webapp/src/components/panels/Settings/Tokens/ApiToken.vue
+++ b/platypush/backend/http/webapp/src/components/panels/Settings/Tokens/ApiToken.vue
@@ -58,12 +58,24 @@
+
API tokens are randomly generated tokens that are stored
@@ -99,6 +111,7 @@ import Description from "./Description";
import Loading from "@/components/Loading";
import Utils from "@/Utils";
import Modal from "@/components/Modal";
+import TokensList from "./TokensList";
export default {
name: "Token",
@@ -107,6 +120,7 @@ export default {
Description,
Loading,
Modal,
+ TokensList,
},
props: {
@@ -119,6 +133,7 @@ export default {
data() {
return {
loading: false,
+ showTokens: false,
token: null,
}
},
@@ -153,10 +168,34 @@ export default {
this.loading = false
}
},
- }
+ },
+
+ watch: {
+ showTokens(value) {
+ if (value) {
+ this.$refs.tokensModal.show()
+ } else {
+ this.$refs.tokensModal.close()
+ }
+ },
+ },
}
diff --git a/platypush/backend/http/webapp/src/components/panels/Settings/Tokens/TokensList.vue b/platypush/backend/http/webapp/src/components/panels/Settings/Tokens/TokensList.vue
new file mode 100644
index 0000000000..ec4a3c37a9
--- /dev/null
+++ b/platypush/backend/http/webapp/src/components/panels/Settings/Tokens/TokensList.vue
@@ -0,0 +1,182 @@
+
+ Are you sure you want to delete this token? No tokens have been generated yet.