diff options
| author | John Estabrook <jestabro@vyos.io> | 2022-10-23 11:07:16 -0500 | 
|---|---|---|
| committer | John Estabrook <jestabro@vyos.io> | 2022-10-25 10:35:48 -0500 | 
| commit | f76a6f68b08fce1feee2dbbb84658b8eede09655 (patch) | |
| tree | 013baed425a22f499ac1a50ffd7c119247ed265d | |
| parent | cbb72ad6d3f5f08ad23c40e29b9463087ca5cade (diff) | |
| download | vyos-1x-f76a6f68b08fce1feee2dbbb84658b8eede09655.tar.gz vyos-1x-f76a6f68b08fce1feee2dbbb84658b8eede09655.zip | |
graphql: T4574: add mutation for requesting JWT token
| -rw-r--r-- | src/services/api/graphql/bindings.py | 8 | ||||
| -rw-r--r-- | src/services/api/graphql/graphql/auth_token_mutation.py | 44 | ||||
| -rw-r--r-- | src/services/api/graphql/graphql/schema/auth_token.graphql | 19 | ||||
| -rw-r--r-- | src/services/api/graphql/libs/token_auth.py | 38 | 
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} | 
