summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Estabrook <jestabro@vyos.io>2022-10-23 11:07:16 -0500
committerJohn Estabrook <jestabro@vyos.io>2022-10-25 10:35:48 -0500
commitf76a6f68b08fce1feee2dbbb84658b8eede09655 (patch)
tree013baed425a22f499ac1a50ffd7c119247ed265d
parentcbb72ad6d3f5f08ad23c40e29b9463087ca5cade (diff)
downloadvyos-1x-f76a6f68b08fce1feee2dbbb84658b8eede09655.tar.gz
vyos-1x-f76a6f68b08fce1feee2dbbb84658b8eede09655.zip
graphql: T4574: add mutation for requesting JWT token
-rw-r--r--src/services/api/graphql/bindings.py8
-rw-r--r--src/services/api/graphql/graphql/auth_token_mutation.py44
-rw-r--r--src/services/api/graphql/graphql/schema/auth_token.graphql19
-rw-r--r--src/services/api/graphql/libs/token_auth.py38
4 files changed, 108 insertions, 1 deletions
diff --git a/src/services/api/graphql/bindings.py b/src/services/api/graphql/bindings.py
index d3cff21c7..aa1ba0eb0 100644
--- a/src/services/api/graphql/bindings.py
+++ b/src/services/api/graphql/bindings.py
@@ -18,9 +18,12 @@ from . graphql.queries import query
from . graphql.mutations import mutation
from . graphql.directives import directives_dict
from . graphql.errors import op_mode_error
+from . graphql.auth_token_mutation import auth_token_mutation
from . generate.schema_from_op_mode import generate_op_mode_definitions
from . generate.schema_from_config_session import generate_config_session_definitions
from . generate.schema_from_composite import generate_composite_definitions
+from . libs.token_auth import init_secret
+from . import state
from ariadne import make_executable_schema, load_schema_from_path, snake_case_fallback_resolvers
def generate_schema():
@@ -30,8 +33,11 @@ def generate_schema():
generate_config_session_definitions()
generate_composite_definitions()
+ if state.settings['app'].state.vyos_auth_type == 'token':
+ init_secret()
+
type_defs = load_schema_from_path(api_schema_dir)
- schema = make_executable_schema(type_defs, query, op_mode_error, mutation, snake_case_fallback_resolvers, directives=directives_dict)
+ schema = make_executable_schema(type_defs, query, op_mode_error, mutation, auth_token_mutation, snake_case_fallback_resolvers, directives=directives_dict)
return schema
diff --git a/src/services/api/graphql/graphql/auth_token_mutation.py b/src/services/api/graphql/graphql/auth_token_mutation.py
new file mode 100644
index 000000000..33779d4f0
--- /dev/null
+++ b/src/services/api/graphql/graphql/auth_token_mutation.py
@@ -0,0 +1,44 @@
+# Copyright 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this library. If not, see <http://www.gnu.org/licenses/>.
+
+import jwt
+from typing import Any, Dict
+from ariadne import ObjectType, UnionType
+from graphql import GraphQLResolveInfo
+
+from .. libs.token_auth import generate_token
+from .. import state
+
+auth_token_mutation = ObjectType("Mutation")
+
+@auth_token_mutation.field('AuthToken')
+def auth_token_resolver(obj: Any, info: GraphQLResolveInfo, data: Dict):
+ # non-nullable fields
+ user = data['username']
+ passwd = data['password']
+
+ secret = state.settings['secret']
+ res = generate_token(user, passwd, secret)
+ if res:
+ data['result'] = res
+ return {
+ "success": True,
+ "data": data
+ }
+
+ return {
+ "success": False,
+ "errors": ['token generation failed']
+ }
diff --git a/src/services/api/graphql/graphql/schema/auth_token.graphql b/src/services/api/graphql/graphql/schema/auth_token.graphql
new file mode 100644
index 000000000..af53a293a
--- /dev/null
+++ b/src/services/api/graphql/graphql/schema/auth_token.graphql
@@ -0,0 +1,19 @@
+
+input AuthTokenInput {
+ username: String!
+ password: String!
+}
+
+type AuthToken {
+ result: Generic
+}
+
+type AuthTokenResult {
+ data: AuthToken
+ success: Boolean!
+ errors: [String]
+}
+
+extend type Mutation {
+ AuthToken(data: AuthTokenInput) : AuthTokenResult
+}
diff --git a/src/services/api/graphql/libs/token_auth.py b/src/services/api/graphql/libs/token_auth.py
new file mode 100644
index 000000000..c53e354b1
--- /dev/null
+++ b/src/services/api/graphql/libs/token_auth.py
@@ -0,0 +1,38 @@
+import jwt
+import uuid
+import pam
+from secrets import token_hex
+
+from .. import state
+
+def _check_passwd_pam(username: str, passwd: str) -> bool:
+ if pam.authenticate(username, passwd):
+ return True
+ return False
+
+def init_secret():
+ secret = token_hex(16)
+ state.settings['secret'] = secret
+
+def generate_token(user: str, passwd: str, secret: str) -> dict:
+ if user is None or passwd is None:
+ return {}
+ if _check_passwd_pam(user, passwd):
+ app = state.settings['app']
+ try:
+ users = app.state.vyos_token_users
+ except AttributeError:
+ app.state.vyos_token_users = {}
+ users = app.state.vyos_token_users
+ user_id = uuid.uuid1().hex
+ payload_data = {'iss': user, 'sub': user_id}
+ secret = state.settings.get('secret')
+ if secret is None:
+ return {
+ "success": False,
+ "errors": ['failed secret generation']
+ }
+ token = jwt.encode(payload=payload_data, key=secret, algorithm="HS256")
+
+ users |= {user_id: user}
+ return {'token': token}