Exemplo n.º 1
0
async def authenticate(request, *args, **kwargs):
    """ - """
    del args, kwargs
    msg = "Error -> Auth -> authenticate"
    with ax_model.scoped_session(msg) as db_session:
        email = request.json.get("email", None)
        password = request.json.get("password", None)

        if not email or not password:
            raise exceptions.AuthenticationFailed("Missing email or password.")

        user = db_session.query(AxUser).filter(
            AxUser.email == email
        ).filter(
            AxUser.is_blocked.is_(False)
        ).first()

        if user is None:
            raise exceptions.AuthenticationFailed("User not found.")

        if not pbkdf2_sha256.verify(password, user.password):
            raise exceptions.AuthenticationFailed("Password is incorrect.")

        await check_if_admin(user_guid=str(user.guid), db_session=db_session)
        await write_perm_cache(db_session=db_session, user_guid=str(user.guid))
        await write_info_cache(user)

        db_session.expunge(user)
        return user
Exemplo n.º 2
0
def sync_field_types():
    """ Read field_types.yaml and check if any fields need to be
        created or modified """
    field_types_yaml = ax_misc.path('field_types.yaml')

    with ax_model.scoped_session() as db_session:

        ax_field_types = db_session.query(AxFieldType).all()
        existing_tags = []
        for field_type in ax_field_types:
            existing_tags.append(field_type.tag)

        if not os.path.isfile(field_types_yaml):
            raise FileNotFoundError(
                'Configuration failed, field_types.yaml not found')

        with open(field_types_yaml, 'r') as stream:
            yaml_vars = yaml.safe_load(stream)
            for item in yaml_vars:
                if isinstance(item, dict) and 'tag' in item:
                    tag = item['tag']
                    if tag in existing_tags:
                        update_field_type(field_types=ax_field_types,
                                          item=item)
                    else:
                        create_field_type(db_session=db_session, item=item)

            db_session.commit()
Exemplo n.º 3
0
def collect_ax_stats():
    """ Collect Ax usage statistics """
    with ax_model.scoped_session() as db_session:
        values = {}
        values['host'] = os.environ.get('AX_HOST', None)
        values['url'] = os.environ.get('AX_URL', None)
        values['dialect'] = os.environ.get('AX_DB_DIALECT', None)
        values['cache'] = os.environ.get('AX_CACHE_MODE', None)
        values['workersNum'] = os.environ.get('AX_SANIC_WORKERS', None)
        values['fingerprint'] = uuid.getnode()
        values['clientGuid'] = ax_auth.client_guid
        values['axVersion'] = os.environ.get('AX_VERSION', None)
        values['usersNum'] = db_session.query(AxUser).filter(
            AxUser.is_group.is_(False)).count()
        values['formsNum'] = db_session.query(AxForm).filter(
            AxForm.is_folder.is_(False)).count()
        # dbSize - #TODO how to do it?
        # sslUsed
        if os.environ.get('SSL_CERT_ABSOLUTE_PATH', None):
            values['sslUsed'] = True

        # actionCodeUsed
        if db_session.query(AxAction).filter(
                AxAction.code.isnot(None)).first():
            values['actionCodeUsed'] = True

        # gridCodeUsed
        if db_session.query(AxGrid).filter(AxGrid.code.isnot(None)).first():
            values['gridCodeUsed'] = True

        # roleCodeUsed
        if db_session.query(AxRole).filter(AxRole.code.isnot(None)).first():
            values['roleCodeUsed'] = True

        # adminEmails
        admin_emails = []
        admin_group = db_session.query(AxUser).filter(
            AxUser.is_admin.is_(True)).filter(
                AxUser.is_group.is_(True)).first()
        if admin_group:
            for usr in admin_group.users:
                if usr.email != '*****@*****.**':
                    admin_emails.append(usr.email)

        values['adminEmails'] = ", ".join(admin_emails)

        # fieldUsage
        # ax_fields = db_session.query(AxField.field_type_tag).filter(
        #     AxField.is_tab.is_(False)
        # ).all()
        # field_dict = {}
        # for fld in ax_fields:
        #     if fld.field_type_tag not in field_dict:
        #         field_dict[fld.field_type_tag] = 1
        #     else:
        #         field_dict[fld.field_type_tag] += 1
        # values['fieldUsage'] = field_dict
        this.stats = values
Exemplo n.º 4
0
def database_fits_metadata() -> None:
    """Compare yaml db version and version in actual database"""
    with ax_model.scoped_session() as db_session:
        alembic_version = db_session.query(AxAlembicVersion).first()
        version = alembic_version.version_num
        yaml_version = os.environ.get('AX_DB_REVISION')
        if yaml_version == version:
            return True
        return False
Exemplo n.º 5
0
        async def db_file_viewer(   # pylint: disable=unused-variable
                request, form_guid, row_guid, field_guid, file_name, user):  # pylint: disable=unused-variable
            """ Used to display files that are stored in database.
                Used in fields like AxImageCropDb"""
            current_user = user
            del request, form_guid, file_name
            with ax_model.scoped_session("routes.db_file_viewer") as db_session:
                safe_row_guid = str(uuid.UUID(str(row_guid)))
                ax_field = db_session.query(AxField).filter(
                    AxField.guid == uuid.UUID(field_guid)
                ).first()

                # first we select only guid and axState to know, if user have
                # access
                row_result = await ax_dialects.dialect.select_one(
                    db_session=db_session,
                    form=ax_field.form,
                    fields_list=[],
                    row_guid=safe_row_guid)

                state_name = row_result[0]['axState']
                state_guid = await ax_auth.get_state_guid(
                    ax_form=ax_field.form,
                    state_name=state_name)

                user_guid = current_user.get(
                    'user_id', None) if current_user else None
                user_is_admin = current_user.get(
                    'is_admin', False) if current_user else False
                allowed_field_dict = await ax_auth.get_allowed_fields_dict(
                    ax_form=ax_field.form,
                    user_guid=user_guid,
                    state_guid=state_guid)

                field_guid = str(ax_field.guid)
                if not user_is_admin:
                    if (field_guid not in allowed_field_dict or
                            allowed_field_dict[field_guid] == 0 or
                            allowed_field_dict[field_guid] is None):
                        email = current_user.get('email', None)
                        msg = (
                            f'Error in db_file_viewer. ',
                            f'not allowed for user [{email}]'
                        )
                        logger.error(msg)
                        return response.text("", status=403)

                field_value = await ax_dialects.dialect.select_field(
                    db_session=db_session,
                    form_db_name=ax_field.form.db_name,
                    field_db_name=ax_field.db_name,
                    row_guid=safe_row_guid)

                return response.raw(
                    field_value, content_type='application/octet-stream')
Exemplo n.º 6
0
async def retrieve_refresh_token(request, user_id, *args, **kwargs):
    """ Get refresh token from cache and refresh all perms cache for user """
    del args, kwargs
    device_guid = request.json['deviceGuid']
    key = f'refresh_token_{device_guid}_{user_id}'
    refresh_token = await ax_cache.cache.get(key)

    if refresh_token:
        with ax_model.scoped_session("Auth.store_refresh_token") as db_session:
            await check_if_admin(user_guid=user_id, db_session=db_session)
            await write_perm_cache(db_session=db_session, user_guid=user_id)

    return refresh_token
Exemplo n.º 7
0
def create_tables() -> None:
    """Create database and create baseline version in Alembic"""
    with ax_model.scoped_session("migration -> create_tables") as db_session:
        if os.environ.get('AX_DB_REVISION') is None:
            msg = 'Cant find AX_DB_REVISION in enviroment variables or app.yaml'
            logger.error(msg)
            raise Exception(msg)

        # ax_model.Base.query = db_session.query_property()
        ax_model.Base.metadata.create_all(ax_model.engine)
        first_version = AxAlembicVersion()
        first_version.version_num = os.environ.get('AX_DB_REVISION')
        db_session.add(first_version)

        logger.info('Ax tables not found. Creating database tables.')
        return True
Exemplo n.º 8
0
    def custom_query(self, sql, variables=None):
        """ Executes any SQL. Used in action python code.
        This method is SYNC, leave it so

        Args:
            db_session: SqlAlchemy session
            sql (str): Any sql that needs to be executed
            variables (Dict): Arguments used in sql query

        Returns:
            List(Dict(column_name: value)): result of SQL query
        """

        with ax_model.scoped_session("SQL - custom_query") as db_session:
            res = db_session.execute(sql, variables)
            if res and res.returns_rows:
                return res.fetchall()
            return res
Exemplo n.º 9
0
def create_default_pages():
    """ Create default page """
    with ax_model.scoped_session(
            "migration -> create_default_pages") as db_session:
        index_page = AxPage()
        index_page.name = "Index Page"
        index_page.code = (f"<h1>Welcome to Ax pages</h1>\n"
                           f"<br/><br/>Hello world")
        db_session.add(index_page)

        all_user = db_session.query(AxUser).filter(
            AxUser.is_all_users.is_(True)).first()

        p2u = AxPage2Users()
        p2u.page_guid = index_page.guid
        p2u.user_guid = all_user.guid
        db_session.add(p2u)

        db_session.commit()
Exemplo n.º 10
0
def create_default_users():
    """ Create Admin group, Default admin, Everyone, All users """
    msg = "Error -> Migration -> create_default_users"
    with ax_model.scoped_session(msg) as db_session:
        # Admin group
        admin_group = AxUser()
        # admin_group.short_name = 'users.admin-group-name'
        admin_group.short_name = 'Administrators'
        admin_group.is_group = True
        admin_group.is_admin = True
        db_session.add(admin_group)

        # Default admin
        admin = AxUser()
        admin.email = '*****@*****.**'
        admin.password = pbkdf2_sha256.hash('deleteme')
        admin.name = 'Default Administrator. Delete this user.'
        admin.short_name = 'Default Admin'
        db_session.add(admin)

        db_session.commit()

        group_user = AxGroup2Users()
        group_user.group_guid = admin_group.guid
        group_user.user_guid = admin.guid
        db_session.add(group_user)

        # Everyone
        everyone_group = AxUser()
        # everyone_group.short_name = 'users.everyone-group-name'
        everyone_group.short_name = 'Everyone'
        everyone_group.is_group = True
        everyone_group.is_everyone = True
        db_session.add(everyone_group)

        # All users
        all_group = AxUser()
        # all_group.short_name = 'users.all-users-group-name'
        all_group.short_name = 'All users'
        all_group.is_group = True
        all_group.is_all_users = True
        db_session.add(all_group)
        db_session.commit()
Exemplo n.º 11
0
async def retrieve_user(request, payload, *args, **kwargs):
    """ Get user info. This info is transfered into routes with inject_user """
    del request, args, kwargs
    if payload:
        user_id = payload.get('user_id') or None

        if not ax_misc.string_is_guid(user_id):
            return None

        email = await ax_cache.cache.get(f'user_email_{user_id}')

        if not email:
            msg = "Auth -> retrieve_user"
            with ax_model.scoped_session(msg) as db_session:
                user = db_session.query(AxUser).filter(
                    AxUser.guid == ax_misc.guid_or_none(user_id)
                ).first()

                if user is not None:
                    # raise exceptions.AuthenticationFailed("User not found.")
                    await check_if_admin(
                        user_guid=user_id, db_session=db_session)
                    await write_perm_cache(
                        db_session=db_session, user_guid=user_id)
                    await write_info_cache(user)

        email = await ax_cache.cache.get(f'user_email_{user_id}')
        short_name = await ax_cache.cache.get(f'user_short_name_{user_id}')
        is_admin = await ax_cache.cache.get(f'user_is_admin_{user_id}')

        user = {
            "user_id": str(user_id),
            "is_admin": is_admin,
            "short_name": short_name,
            "email": email
        }
        return user
    else:
        return None
Exemplo n.º 12
0
def init_auth(sanic_app, secret="This is big secret, set me in app.yaml"):
    """
    Initiate sanic-jwt module

    Copyright (C) 2020 Mikhail Marenov - All Rights Reserved
    You MAY NOT CHANGE source code of Ax workflow without writen permission
    author. If you change source code in order to activate PRO features -
    YOU MAY BE SUBJECT TO HEAVY CIVIL PENALTIES. THESE INCLUDE MONETARY
    DAMAGES, COURT COSTS, AND ATTORNEYS FEES INCURRED
    Please read LICENSE.md for more information.
    """

    delta = 60  # seconds
    initialize(sanic_app,
               authenticate=authenticate,
               configuration_class=AxConfiguration,
               refresh_token_enabled=True,
               store_refresh_token=store_refresh_token,
               retrieve_refresh_token=retrieve_refresh_token,
               retrieve_user=retrieve_user,
               expiration_delta=delta,
               cookie_access_token_name='ax_auth',
               cookie_set=True,
               cookie_strict=False,
               login_redirect_url='/signin',
               secret=secret)

    with ax_model.scoped_session("init_auth - ERROR") as db_session:
        apply_lise(db_session)

        # Write cache form Everyone group
        asyncio.get_event_loop().run_until_complete(write_perm_cache(
            db_session=db_session, user_guid=None))

        # Write cache for dynamic roles
        asyncio.get_event_loop().run_until_complete(write_dynamic_roles_cache(
            db_session=db_session))
Exemplo n.º 13
0
    async def dispatch_request(self, request, *args, **kwargs):
        """ Sanic view request dispatch.  """
        start_time = time.time()  # TODO this is debug profile

        with ax_model.scoped_session("GQL error -") as db_session:
            try:
                request_method = request.method.lower()
                data = self.parse_body(request)

                show_graphiql = request_method == 'get' and self.should_display_graphiql(
                    request)
                catch = show_graphiql

                pretty = self.pretty or show_graphiql or request.args.get(
                    'pretty')

                user = kwargs['user'] or None

                # auth_header = request.headers['authorization']
                # print(auth_header)

                ax_context = self.get_context(request)
                ax_context.update({'session': db_session})
                ax_context.update({'user': user})

                if request_method != 'options':
                    execution_results, all_params = run_http_query(
                        ax_schema.schema,
                        request_method,
                        data,
                        query_data=request.args,
                        batch_enabled=self.batch,
                        catch=catch,

                        # Execute options
                        return_promise=self._enable_async,
                        root_value=self.get_root_value(request),
                        context_value=ax_context,
                        middleware=self.get_middleware(request),
                        executor=self.get_executor(request),
                    )  # pylint: disable=unused-argument
                    del all_params
                    awaited_execution_results = await Promise.all(
                        execution_results)
                    result, status_code = encode_execution_results(
                        awaited_execution_results,
                        is_batch=isinstance(data, list),
                        format_error=self.format_error,
                        encode=partial(self.encode, pretty=pretty))
                    log_reguest(request, (time.time() - start_time))

                    return HTTPResponse(result,
                                        status=status_code,
                                        content_type='application/json')
                else:
                    log_reguest(request, (time.time() - start_time))
                    return self.process_preflight(request)

            except HttpQueryError as err:
                logger.exception(f'graqlView -> {err}')
                return HTTPResponse(self.encode(
                    {'errors': [default_format_error(err)]}),
                                    status=err.status_code,
                                    headers=err.headers,
                                    content_type='application/json')
            except Exception as err:  # pylint: disable=broad-except
                logger.exception(f'graqlView -> {err}')
Exemplo n.º 14
0
def init_schema_standalone():
    """ Initiate GQL schema without db_session """
    error_msg = "Schema -> init_schema. Error initiating GraphQL shcema."
    with ax_model.scoped_session(error_msg) as db_session:
        init_schema(db_session)
Exemplo n.º 15
0
        async def file_viewer(  # pylint: disable=unused-variable
                request, form_guid, row_guid, field_guid, file_guid, file_name,\
                user=None):
            """ Used to display files uploaded and stored on disk.
                Displays temp files too. Used in all fields with upload"""
            del request
            current_user = user
            with ax_model.scoped_session(
                    "routes -> file_viewer") as db_session:
                # if row_guid is null -> display from /tmp without permissions
                if not row_guid or row_guid == 'null':
                    tmp_dir = os.path.join(ax_misc.tmp_root_dir, file_guid)
                    file_name = os.listdir(tmp_dir)[0]
                    temp_path = os.path.join(tmp_dir, file_name)
                    return await response.file(temp_path)

                # get AxForm with row values
                ax_form = db_session.query(AxForm).filter(
                    AxForm.guid == uuid.UUID(form_guid)).first()
                ax_form = await form_schema.set_form_values(
                    db_session=db_session,
                    ax_form=ax_form,
                    row_guid=row_guid,
                    current_user=current_user)

                # Get values from row, field
                field_values = None
                for field in ax_form.fields:
                    if field.guid == uuid.UUID(field_guid):
                        if field.value:
                            field_values = json.loads(field.value)

                            if type(field_values) is str:
                                try:
                                    field_values = json.loads(field_values)
                                except:
                                    pass

                # Find requested file in value
                the_file = None
                for file in field_values:
                    if file['guid'] == file_guid:
                        the_file = file

                if not the_file:
                    return response.text("", status=404)

                state_guid = await ax_auth.get_state_guid(
                    ax_form=ax_form, state_name=ax_form.current_state_name)

                user_guid = current_user.get('user_id',
                                             None) if current_user else None
                user_is_admin = current_user.get(
                    'is_admin', False) if current_user else False
                allowed_field_dict = await ax_auth.get_allowed_fields_dict(
                    ax_form=ax_form,
                    user_guid=user_guid,
                    state_guid=state_guid)

                if not user_is_admin:
                    if (field_guid not in allowed_field_dict
                            or allowed_field_dict[field_guid] == 0
                            or allowed_field_dict[field_guid] is None):
                        email = current_user['email']
                        msg = (f'Error in file_viewer. ',
                               f'not allowed for user [{email}]')
                        logger.error(msg)
                        return response.text("", status=403)

                # if file exists -> return file
                row_guid_str = str(uuid.UUID(row_guid))
                file_path = os.path.join(ax_misc.uploads_root_dir,
                                         'form_row_field_file', form_guid,
                                         row_guid_str, field_guid,
                                         the_file['guid'], the_file['name'])
                if not os.path.lexists(file_path):
                    return response.text("", status=404)
                return await response.file(file_path)