summaryrefslogtreecommitdiff
path: root/src/services/api/graphql
diff options
context:
space:
mode:
Diffstat (limited to 'src/services/api/graphql')
-rw-r--r--src/services/api/graphql/bindings.py29
-rw-r--r--src/services/api/graphql/graphql/auth_token_mutation.py31
-rw-r--r--src/services/api/graphql/graphql/mutations.py71
-rw-r--r--src/services/api/graphql/graphql/queries.py71
-rw-r--r--src/services/api/graphql/libs/key_auth.py4
-rw-r--r--src/services/api/graphql/libs/token_auth.py10
-rw-r--r--src/services/api/graphql/routers.py41
-rw-r--r--src/services/api/graphql/session/session.py33
8 files changed, 152 insertions, 138 deletions
diff --git a/src/services/api/graphql/bindings.py b/src/services/api/graphql/bindings.py
index 93dd0fbfb..ebf745f32 100644
--- a/src/services/api/graphql/bindings.py
+++ b/src/services/api/graphql/bindings.py
@@ -20,18 +20,18 @@ from ariadne import make_executable_schema
from ariadne import load_schema_from_path
from ariadne import snake_case_fallback_resolvers
-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 . libs.token_auth import init_secret
+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 .libs.token_auth import init_secret
-from .. session import SessionState
+from ..session import SessionState
def generate_schema():
- state = SessionState()
+ state = SessionState()
api_schema_dir = vyos.defaults.directories['api_schema']
if state.auth_type == 'token':
@@ -39,9 +39,14 @@ def generate_schema():
type_defs = load_schema_from_path(api_schema_dir)
- schema = make_executable_schema(type_defs, query, op_mode_error,
- mutation, auth_token_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
index 164960217..c74364603 100644
--- a/src/services/api/graphql/graphql/auth_token_mutation.py
+++ b/src/services/api/graphql/graphql/auth_token_mutation.py
@@ -19,11 +19,12 @@ from typing import Dict
from ariadne import ObjectType
from graphql import GraphQLResolveInfo
-from .. libs.token_auth import generate_token
-from .. session.session import get_user_info
-from ... session import SessionState
+from ..libs.token_auth import generate_token
+from ..session.session import get_user_info
+from ...session import SessionState
+
+auth_token_mutation = ObjectType('Mutation')
-auth_token_mutation = ObjectType("Mutation")
@auth_token_mutation.field('AuthToken')
def auth_token_resolver(obj: Any, info: GraphQLResolveInfo, data: Dict):
@@ -35,8 +36,9 @@ def auth_token_resolver(obj: Any, info: GraphQLResolveInfo, data: Dict):
secret = getattr(state, 'secret', '')
exp_interval = int(state.token_exp)
- expiration = (datetime.datetime.now(tz=datetime.timezone.utc) +
- datetime.timedelta(seconds=exp_interval))
+ expiration = datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(
+ seconds=exp_interval
+ )
res = generate_token(user, passwd, secret, expiration)
try:
@@ -46,18 +48,9 @@ def auth_token_resolver(obj: Any, info: GraphQLResolveInfo, data: Dict):
pass
if 'token' in res:
data['result'] = res
- return {
- "success": True,
- "data": data
- }
+ return {'success': True, 'data': data}
if 'errors' in res:
- return {
- "success": False,
- "errors": res['errors']
- }
-
- return {
- "success": False,
- "errors": ['token generation failed']
- }
+ return {'success': False, 'errors': res['errors']}
+
+ return {'success': False, 'errors': ['token generation failed']}
diff --git a/src/services/api/graphql/graphql/mutations.py b/src/services/api/graphql/graphql/mutations.py
index 62031ada3..0b391c070 100644
--- a/src/services/api/graphql/graphql/mutations.py
+++ b/src/services/api/graphql/graphql/mutations.py
@@ -14,20 +14,23 @@
# along with this library. If not, see <http://www.gnu.org/licenses/>.
from importlib import import_module
-from ariadne import ObjectType, convert_camel_case_to_snake
-from makefun import with_signature
# used below by func_sig
-from typing import Any, Dict, Optional # pylint: disable=W0611
-from graphql import GraphQLResolveInfo # pylint: disable=W0611
+from typing import Any, Dict, Optional # pylint: disable=W0611 # noqa: F401
+from graphql import GraphQLResolveInfo # pylint: disable=W0611 # noqa: F401
+
+from ariadne import ObjectType, convert_camel_case_to_snake
+from makefun import with_signature
-from ... session import SessionState
-from .. libs import key_auth
-from .. session.session import Session
-from .. session.errors.op_mode_errors import op_mode_err_msg, op_mode_err_code
from vyos.opmode import Error as OpModeError
-mutation = ObjectType("Mutation")
+from ...session import SessionState
+from ..libs import key_auth
+from ..session.session import Session
+from ..session.errors.op_mode_errors import op_mode_err_msg, op_mode_err_code
+
+mutation = ObjectType('Mutation')
+
def make_mutation_resolver(mutation_name, class_name, session_func):
"""Dynamically generate a resolver for the mutation named in the
@@ -59,10 +62,7 @@ def make_mutation_resolver(mutation_name, class_name, session_func):
auth = key_auth.auth_required(key)
if auth is None:
- return {
- "success": False,
- "errors": ['invalid API key']
- }
+ return {'success': False, 'errors': ['invalid API key']}
# We are finished with the 'key' entry, and may remove so as to
# pass the rest of data (if any) to function.
@@ -77,14 +77,8 @@ def make_mutation_resolver(mutation_name, class_name, session_func):
if user is None:
error = info.context.get('error')
if error is not None:
- return {
- "success": False,
- "errors": [error]
- }
- return {
- "success": False,
- "errors": ['not authenticated']
- }
+ return {'success': False, 'errors': [error]}
+ return {'success': False, 'errors': ['not authenticated']}
else:
# AtrributeError will have already been raised if no
# auth_type; validation and defaultValue ensure it is
@@ -106,35 +100,36 @@ def make_mutation_resolver(mutation_name, class_name, session_func):
result = method()
data['result'] = result
- return {
- "success": True,
- "data": data
- }
+ return {'success': True, 'data': data}
except OpModeError as e:
typename = type(e).__name__
msg = str(e)
return {
- "success": False,
- "errore": ['op_mode_error'],
- "op_mode_error": {"name": f"{typename}",
- "message": msg if msg else op_mode_err_msg.get(typename, "Unknown"),
- "vyos_code": op_mode_err_code.get(typename, 9999)}
+ 'success': False,
+ 'errore': ['op_mode_error'],
+ 'op_mode_error': {
+ 'name': f'{typename}',
+ 'message': msg if msg else op_mode_err_msg.get(typename, 'Unknown'),
+ 'vyos_code': op_mode_err_code.get(typename, 9999),
+ },
}
except Exception as error:
- return {
- "success": False,
- "errors": [repr(error)]
- }
+ return {'success': False, 'errors': [repr(error)]}
return func_impl
+
def make_config_session_mutation_resolver(mutation_name):
- return make_mutation_resolver(mutation_name, mutation_name,
- convert_camel_case_to_snake(mutation_name))
+ return make_mutation_resolver(
+ mutation_name, mutation_name, convert_camel_case_to_snake(mutation_name)
+ )
+
def make_gen_op_mutation_resolver(mutation_name):
return make_mutation_resolver(mutation_name, mutation_name, 'gen_op_mutation')
+
def make_composite_mutation_resolver(mutation_name):
- return make_mutation_resolver(mutation_name, mutation_name,
- convert_camel_case_to_snake(mutation_name))
+ return make_mutation_resolver(
+ mutation_name, mutation_name, convert_camel_case_to_snake(mutation_name)
+ )
diff --git a/src/services/api/graphql/graphql/queries.py b/src/services/api/graphql/graphql/queries.py
index 1e9036574..9303fe909 100644
--- a/src/services/api/graphql/graphql/queries.py
+++ b/src/services/api/graphql/graphql/queries.py
@@ -14,20 +14,23 @@
# along with this library. If not, see <http://www.gnu.org/licenses/>.
from importlib import import_module
-from ariadne import ObjectType, convert_camel_case_to_snake
-from makefun import with_signature
# used below by func_sig
-from typing import Any, Dict, Optional # pylint: disable=W0611
-from graphql import GraphQLResolveInfo # pylint: disable=W0611
+from typing import Any, Dict, Optional # pylint: disable=W0611 # noqa: F401
+from graphql import GraphQLResolveInfo # pylint: disable=W0611 # noqa: F401
+
+from ariadne import ObjectType, convert_camel_case_to_snake
+from makefun import with_signature
-from ... session import SessionState
-from .. libs import key_auth
-from .. session.session import Session
-from .. session.errors.op_mode_errors import op_mode_err_msg, op_mode_err_code
from vyos.opmode import Error as OpModeError
-query = ObjectType("Query")
+from ...session import SessionState
+from ..libs import key_auth
+from ..session.session import Session
+from ..session.errors.op_mode_errors import op_mode_err_msg, op_mode_err_code
+
+query = ObjectType('Query')
+
def make_query_resolver(query_name, class_name, session_func):
"""Dynamically generate a resolver for the query named in the
@@ -59,10 +62,7 @@ def make_query_resolver(query_name, class_name, session_func):
auth = key_auth.auth_required(key)
if auth is None:
- return {
- "success": False,
- "errors": ['invalid API key']
- }
+ return {'success': False, 'errors': ['invalid API key']}
# We are finished with the 'key' entry, and may remove so as to
# pass the rest of data (if any) to function.
@@ -77,14 +77,8 @@ def make_query_resolver(query_name, class_name, session_func):
if user is None:
error = info.context.get('error')
if error is not None:
- return {
- "success": False,
- "errors": [error]
- }
- return {
- "success": False,
- "errors": ['not authenticated']
- }
+ return {'success': False, 'errors': [error]}
+ return {'success': False, 'errors': ['not authenticated']}
else:
# AtrributeError will have already been raised if no
# auth_type; validation and defaultValue ensure it is
@@ -106,35 +100,36 @@ def make_query_resolver(query_name, class_name, session_func):
result = method()
data['result'] = result
- return {
- "success": True,
- "data": data
- }
+ return {'success': True, 'data': data}
except OpModeError as e:
typename = type(e).__name__
msg = str(e)
return {
- "success": False,
- "errors": ['op_mode_error'],
- "op_mode_error": {"name": f"{typename}",
- "message": msg if msg else op_mode_err_msg.get(typename, "Unknown"),
- "vyos_code": op_mode_err_code.get(typename, 9999)}
+ 'success': False,
+ 'errors': ['op_mode_error'],
+ 'op_mode_error': {
+ 'name': f'{typename}',
+ 'message': msg if msg else op_mode_err_msg.get(typename, 'Unknown'),
+ 'vyos_code': op_mode_err_code.get(typename, 9999),
+ },
}
except Exception as error:
- return {
- "success": False,
- "errors": [repr(error)]
- }
+ return {'success': False, 'errors': [repr(error)]}
return func_impl
+
def make_config_session_query_resolver(query_name):
- return make_query_resolver(query_name, query_name,
- convert_camel_case_to_snake(query_name))
+ return make_query_resolver(
+ query_name, query_name, convert_camel_case_to_snake(query_name)
+ )
+
def make_gen_op_query_resolver(query_name):
return make_query_resolver(query_name, query_name, 'gen_op_query')
+
def make_composite_query_resolver(query_name):
- return make_query_resolver(query_name, query_name,
- convert_camel_case_to_snake(query_name))
+ return make_query_resolver(
+ query_name, query_name, convert_camel_case_to_snake(query_name)
+ )
diff --git a/src/services/api/graphql/libs/key_auth.py b/src/services/api/graphql/libs/key_auth.py
index 9e49a1203..ffd7f32b2 100644
--- a/src/services/api/graphql/libs/key_auth.py
+++ b/src/services/api/graphql/libs/key_auth.py
@@ -14,7 +14,8 @@
# along with this library. If not, see <http://www.gnu.org/licenses/>.
-from ... session import SessionState
+from ...session import SessionState
+
def check_auth(key_list, key):
if not key_list:
@@ -25,6 +26,7 @@ def check_auth(key_list, key):
key_id = k['id']
return key_id
+
def auth_required(key):
state = SessionState()
api_keys = None
diff --git a/src/services/api/graphql/libs/token_auth.py b/src/services/api/graphql/libs/token_auth.py
index 2d772e035..4f743a096 100644
--- a/src/services/api/graphql/libs/token_auth.py
+++ b/src/services/api/graphql/libs/token_auth.py
@@ -19,7 +19,7 @@ import uuid
import pam
from secrets import token_hex
-from ... session import SessionState
+from ...session import SessionState
def _check_passwd_pam(username: str, passwd: str) -> bool:
@@ -48,13 +48,13 @@ def generate_token(user: str, passwd: str, secret: str, exp: int) -> dict:
payload_data = {'iss': user, 'sub': user_id, 'exp': exp}
secret = getattr(state, 'secret', None)
if secret is None:
- return {"errors": ['missing secret']}
- token = jwt.encode(payload=payload_data, key=secret, algorithm="HS256")
+ return {'errors': ['missing secret']}
+ token = jwt.encode(payload=payload_data, key=secret, algorithm='HS256')
users |= {user_id: user}
return {'token': token}
else:
- return {"errors": ['failed pam authentication']}
+ return {'errors': ['failed pam authentication']}
def get_user_context(request):
@@ -70,7 +70,7 @@ def get_user_context(request):
try:
secret = getattr(state, 'secret', None)
- payload = jwt.decode(token, secret, algorithms=["HS256"])
+ payload = jwt.decode(token, secret, algorithms=['HS256'])
user_id: str = payload.get('sub')
if user_id is None:
return context
diff --git a/src/services/api/graphql/routers.py b/src/services/api/graphql/routers.py
index f02380cdc..ed3ee1e8c 100644
--- a/src/services/api/graphql/routers.py
+++ b/src/services/api/graphql/routers.py
@@ -26,14 +26,15 @@ if typing.TYPE_CHECKING:
from fastapi import FastAPI
-def graphql_init(app: "FastAPI"):
- from .. session import SessionState
+def graphql_init(app: 'FastAPI'):
+ from ..session import SessionState
from .libs.token_auth import get_user_context
state = SessionState()
# import after initializaion of state
from .bindings import generate_schema
+
schema = generate_schema()
in_spec = state.introspection
@@ -44,21 +45,33 @@ def graphql_init(app: "FastAPI"):
if state.origins:
origins = state.origins
- app.add_route('/graphql', CORSMiddleware(GraphQL(schema,
- context_value=get_user_context,
- debug=True,
- introspection=in_spec),
- allow_origins=origins,
- allow_methods=("GET", "POST", "OPTIONS"),
- allow_headers=("Authorization",)))
+ app.add_route(
+ '/graphql',
+ CORSMiddleware(
+ GraphQL(
+ schema,
+ context_value=get_user_context,
+ debug=True,
+ introspection=in_spec,
+ ),
+ allow_origins=origins,
+ allow_methods=('GET', 'POST', 'OPTIONS'),
+ allow_headers=('Authorization',),
+ ),
+ )
else:
- app.add_route('/graphql', GraphQL(schema,
- context_value=get_user_context,
- debug=True,
- introspection=in_spec))
+ app.add_route(
+ '/graphql',
+ GraphQL(
+ schema,
+ context_value=get_user_context,
+ debug=True,
+ introspection=in_spec,
+ ),
+ )
-def graphql_clear(app: "FastAPI"):
+def graphql_clear(app: 'FastAPI'):
for r in app.routes:
if r.path == '/graphql':
app.routes.remove(r)
diff --git a/src/services/api/graphql/session/session.py b/src/services/api/graphql/session/session.py
index 6e2875f3c..619534f43 100644
--- a/src/services/api/graphql/session/session.py
+++ b/src/services/api/graphql/session/session.py
@@ -28,34 +28,45 @@ from api.graphql.libs.op_mode import normalize_output
op_mode_include_file = os.path.join(directories['data'], 'op-mode-standardized.json')
-def get_config_dict(path=[], effective=False, key_mangling=None,
- get_first_key=False, no_multi_convert=False,
- no_tag_node_value_mangle=False):
+
+def get_config_dict(
+ path=[],
+ effective=False,
+ key_mangling=None,
+ get_first_key=False,
+ no_multi_convert=False,
+ no_tag_node_value_mangle=False,
+):
config = Config()
- return config.get_config_dict(path=path, effective=effective,
- key_mangling=key_mangling,
- get_first_key=get_first_key,
- no_multi_convert=no_multi_convert,
- no_tag_node_value_mangle=no_tag_node_value_mangle)
+ return config.get_config_dict(
+ path=path,
+ effective=effective,
+ key_mangling=key_mangling,
+ get_first_key=get_first_key,
+ no_multi_convert=no_multi_convert,
+ no_tag_node_value_mangle=no_tag_node_value_mangle,
+ )
+
def get_user_info(user):
user_info = {}
- info = get_config_dict(['system', 'login', 'user', user],
- get_first_key=True)
+ info = get_config_dict(['system', 'login', 'user', user], get_first_key=True)
if not info:
- raise ValueError("No such user")
+ raise ValueError('No such user')
user_info['user'] = user
user_info['full_name'] = info.get('full-name', '')
return user_info
+
class Session:
"""
Wrapper for calling configsession functions based on GraphQL requests.
Non-nullable fields in the respective schema allow avoiding a key check
in 'data'.
"""
+
def __init__(self, session, data):
self._session = session
self._data = data