以编程方式创建用户

时间:2018-01-10 16:43:45

标签: superset

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

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

2 个答案:

答案 0 :(得分:1)

如果您打算以编程方式创建用户,那么最好的办法是考虑覆盖基础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

答案 1 :(得分:1)

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

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()

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

{{1}}