以编程方式创建用户

Creating a user programmatically

如何在 Superset 中以编程方式创建具有已定义角色的用户?

假设我有一个电子邮件登录页面。用户注册。注册后,我想以编程方式将具有 gamma 角色的用户创建到 Superset 实例中。

如果您打算以编程方式创建用户,那么您最好的选择可能是考虑覆盖底层 Flask-App-Builder (F.A.B.) 框架的 SecurityManager - Superset 基于.

可在此处找到文档:https://github.com/dpgaspar/Flask-AppBuilder/blob/master/docs/security.rst#your-custom-security

话虽如此 - 感觉您的问题实际上是关于如何允许用户在 Superset 中自行注册并默认为他们提供 Gamma 角色。

这是一个明确的 FAB 地址用例,您应该能够通过配置实现这一点:http://flask-appbuilder.readthedocs.io/en/latest/user_registration.html

我使用 a reply from superset git 并创建了以下 类 来添加具有角色的用户。 您需要一个包含用户信息和您要应用的角色名称的 json 文件。像这样:

[
    {
        "first_name": "a", 
        "last_name": "b", 
        "username": "abc", 
        "email": "abc@def.com",
        "password": "123",
        "roles": ["generalViewer","databaseXView"]
    }, 
    {
        "first_name": "b", 
        "last_name": "c", 
        "username": "bcd", 
        "email": "bcd@def.com", 
        "password": "123",
        "roles": ["generalViewer","databaseXView"]
    }
]

因为我们需要使用角色名称(我们可以通过正常导航从超集中获取它们)来设置数据库凭据以获取 role_id。还需要设置任何有权创建另一个用户的登录。

import requests
import json
from bs4 import BeautifulSoup as bs
from bs4 import Comment
from time import sleep
import psycopg2
from typing import List, Tuple
import pandas as pd

class SupersetApi:
    def __init__(self, username=None, password=None):
        self.s = requests.session()
        self.base_url = "http://123.45.67.890:8088/" #superset server ip
        self._csrf = self._getCSRF(self.url('login/'))
        self.headers = {'X-CSRFToken': self._csrf, 'Referer': self.url('login/')}
        # note: does not use headers because of flask_wtf.csrf.validate_csrf
        # if data is dict it is used as form and ends up empty but flask_wtf checks if data ...
        payload = {'username': username, 'password': password, 'csrf_token': self._csrf}
        self.post('login/', payload, None)
        #self.s.post(self.url(), data=payload, headers = {})

    def url(self, url_path):
        return self.base_url + url_path

    def get(self, url_path):
        return self.s.get(self.url(url_path), headers=self.headers)

    def post(self, url_path, data=None, json_data=None, **kwargs):
        kwargs.update({'url': self.url(url_path), 'headers': self.headers})
        if data:
            data['csrf_token'] = self._csrf
            kwargs['data'] = data
        if json_data:
            kwargs['json'] = json_data
        try:
            response = self.s.post(**kwargs)
            statusCode = response.status_code
            if statusCode < 399:
                return True, statusCode
            else:
                print('POST response: {}'.format(statusCode))
                return False, statusCode
        except Exception as e:
            print(e)
            return False, e

    def _getCSRF(self, url_path):
        try:
            response = self.s.get(url_path)
            response.raise_for_status()
        except Exception as e:
            print(e)
            exit()
        soup = bs(response.content, "html.parser")
        for tag in soup.find_all('input', id='csrf_token'):
            csrf_token = tag['value']
        return  csrf_token


class PostgresDB:
    def __init__(self, db_name: str, db_user: str, db_host: str, db_password: str) -> None:
        self.db_name = db_name
        self.db_user = db_user
        self.db_host = db_host
        self.db_password = db_password
        self.conn = None
        self.cur = None

    def connect(self) -> None:
        try: 
            self.conn = psycopg2.connect('dbname={} user={} host={} password={}'.format(
                self.db_name, self.db_user, self.db_host, self.db_password))
            self.cur = self.conn.cursor()
        except Exception as e:
            raise Exception('Couldn\'t connect to the database. Error: {}'.format(e))

    def commit(self) -> None:
        if self.conn is not None:
            self.conn.commit()
        else:
            raise Exception('Connection not opened to commit')

    def close_connection(self) -> None:
        if self.cur is not None or self.conn is not None:
            try:
                self.cur.close()
            except:
                pass
            try:
                self.conn.close()
            except:
                pass
        else:
            print('Connection and Cursor not opened to be closed')

    def get_from_ab_role(self, columns: List, filters: str) -> List:
        roles_table = 'public.ab_role'
        sql_columns = ','.join(columns)
        sql = "SELECT {} FROM {} WHERE {}".format(sql_columns, roles_table, filters)
        self.cur.execute(sql)
        return self.cur.fetchall()


class SupersetUser:
    def __init__(self, user_dict: dict, db: 'PostgresDB'):
        self.first_name = user_dict.get('first_name')
        self.last_name = user_dict.get('last_name')
        self.username = user_dict.get('username')
        self.email = user_dict.get('email')
        self.active = True
        self.password = user_dict.get('password')
        self.roles = self.get_role_id(db, user_dict.get('roles'))

    def get_role_id(self, db: 'PostgresDB', roles: List):
        filter = 'name = '
        filter = filter + "'{}' ".format(roles[0])
        if len(roles) > 1:
            for role in roles[1:]:
                filter = filter + "OR name = '{}' ".format(role)
        ids = []
        for id in db.get_from_ab_role(['id'], filter):
            ids.append(str(id[0]))
        return ids

    def create_user(self, superset_api: SupersetApi) -> Tuple:
        Error_friendly_message = None
        Error_not = True

        url_path = 'users/api/create'
        payload = {'first_name': self.first_name,
                    'last_name': self.last_name,
                    'username': self.username,
                    'email': self.email,
                    'active': self.active,
                    'conf_password': self.password,
                    'password': self.password,
                    'roles': self.roles
                    }

        Error_not, http_response_code = superset_api.post(url_path=url_path, json=payload)
        if Error_not:
            print('User {} created. Corresponding e-mail: {}'.format(self.username, self.email))
            return Error_not, Error_friendly_message, http_response_code
        elif http_response_code == 500:
            Error_not = False
            Error_friendly_message = ('Ops! Something went wrong. Probably already exist an ' 
                                    'user with the same e-mail: {}, or an error with the json variables... '
                                    'All of them must be strings or a list of strings'.format(self.email))
            return Error_not, Error_friendly_message, http_response_code
        else:
            Error_not = False
            Error_friendly_message = 'Ops! Something went wrong. Try again.'
            return Error_not, Error_friendly_message, http_response_code



#file that contains the users to be created
FILE_NAME = 'users.json'
#need credentials from user with admin role to create new user
ADMIN_USR = 'admin'
ADMIN_PSWD = 'adminpassword'
DB_NAME = 'superset_database'
DB_USER = 'superset_user'
DB_HOST = '123.45.67.890'
DB_PSWD = 'superset_password'

superset = SupersetApi(ADMIN_USR, ADMIN_PSWD)
portgre_db = PostgresDB(DB_NAME, DB_USER, DB_HOST, DB_PSWD)
portgre_db.connect()

try:
    with open(FILE_NAME, 'r') as f:
        users = json.load(f)
        print('File successfully read')
except FileNotFoundError as e:
    print(e)

for index, user in enumerate(users):
    userRoles = []
    superset_user = SupersetUser(user, portgre_db)
    Error_not, Error_friendly_message, http_response_code = superset_user.create_user(superset)
    if not Error_not:
        print('Could\'t create user {}.'.format(superset_user.username))
        print(Error_friendly_message)
        print('HTTP Response Code: {}'.format(http_response_code))

portgre_db.close_connection()