def test_csv_import(): auth_client = AuthClient( constants.HOST, verify_certificate=False, username='******', password='******', ) token_data = auth_client.token.new(expiration=300) token = token_data['token'] auth_client.set_token(token) client = ConfdClient( constants.HOST, verify_certificate=False, token=token, ) start = datetime.now() result = upload_csv(client, token_data['metadata']['tenant_uuid']) stop = datetime.now() assert 'created' in result, 'Result should contains the created users:\n{}'.format(result) assert len(result['created']) == 100, 'Should have created 100 users\n{}'.format(result) assert stop - start <= MAX_TIME, "CSV import exceeded max time ({})".format(MAX_TIME) # NOTE(fblackburn): wait until pjsip reload complete before starting next test time.sleep(5)
def validate(self): super().validate() try: auth_client = AuthClient( username=self.username.data, password=self.password.data, **app.config['auth'], ) response = auth_client.token.new(expiration=60 * 60 * 12) auth_client.set_token(response['token']) user = auth_client.users.get(response['metadata']['uuid']) user['password'] = self.password.data user['instance_uuid'] = response['xivo_uuid'] session['user'] = user except HTTPError as e: if unauthorized(e): self.username.errors.append(USERNAME_PASSWORD_ERROR) self.password.errors.append(USERNAME_PASSWORD_ERROR) return False raise ValidationError(l_('Error with Wazo authentication server: %(error)s', error=e.message)) except requests.ConnectionError: raise ValidationError(l_('Wazo authentication server connection error')) self.user = UserUI(response['token'], response['auth_id']) self.user.set_tenant(response['metadata']['tenant_uuid']) return True
def do_migration(config): with closing(psycopg2.connect(config['db_uri'])) as conn: tenants = _build_tenant_bodies_from_entities(conn.cursor()) auth_client = AuthClient(**config['auth']) token = auth_client.token.new(expiration=36000)['token'] auth_client.set_token(token) existing_tenants = _get_existing_tenants(auth_client) for tenant in tenants: if tenant['name'] in existing_tenants.keys(): continue tenant = auth_client.tenants.new(**tenant) existing_tenants[tenant['name']] = tenant['uuid'] with closing(psycopg2.connect(config['db_uri'])) as conn: cursor = conn.cursor() for name, tenant_uuid in existing_tenants.items(): if not name: # wazo-auth allow tenants without names but those do not map to entities continue _upsert_tenant(cursor, tenant_uuid) conn.commit() _update_entity_tenant_uuid(cursor, name, tenant_uuid) conn.commit() auth_client.token.revoke(token)
def migrate_tenants(): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=5 * 60)['token'] auth_client.set_token(token) body = [ {"owner_user_uuid": user['uuid'], "owner_tenant_uuid": user['tenant_uuid']} for user in auth_client.users.list(recurse=True)["items"] ] with _migration_plugin(config['webhookd']): url = 'https://{host}:{port}/{version}/tenant-migration'.format(**config['webhookd']) result = requests.post( url, data=json.dumps(body), headers={'X-Auth-Token': token, 'Content-Type': 'application/json'}, verify=False, ) if result.status_code != 200: print( 'webhookd tenant migration failed, status-code {}:\n' '{}\ncheck /var/log/wazo-webhookd.log for more info' .format(result.status_code, result.text) ) sys.exit(2)
def migrate_tenants(): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=5 * 60)['token'] auth_client.set_token(token) auth_tenants = auth_client.tenants.list() body = [{ 'uuid': t['uuid'], 'name': t['name'] } for t in auth_tenants['items']] with _migration_plugin(config['dird']): url = 'https://{host}:{port}/{version}/phonebook_move_tenant'.format( **config['dird']) result = requests.post( url, data=json.dumps(body), headers={ 'X-Auth-Token': token, 'Content-Type': 'application/json' }, verify=False, ) if result.status_code != 200: print( 'dird tenant migration failed, check /var/log/wazo-dird.log for more info' ) sys.exit(2)
def migrate_tenants(): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new(expiration=5 * 60)['token'] auth_client.set_token(token) confd = ConfdClient(token=token, **config['confd']) body = { 'contexts': [ {"context": context['name'], "tenant_uuid": context['tenant_uuid']} for context in confd.contexts.list(recurse=True)["items"] ] } with _migration_plugin(config['call_logd']): url = 'https://{host}:{port}/{version}/tenant-migration'.format(**config['call_logd']) result = requests.post( url, data=json.dumps(body), headers={'X-Auth-Token': token, 'Content-Type': 'application/json'}, verify=False, ) if result.status_code != 200: print( 'call_logd tenant migration failed, status-code {}:\n' '{}\ncheck /var/log/wazo-call-logd.log for more info' .format(result.status_code, result.text) ) sys.exit(2)
class WazoAuthKeys(App): DEFAULT_VERBOSE_LEVEL = 0 def __init__(self): super().__init__( description='A wrapper to wazo-auth-cli to manage internal users', command_manager=CommandManager('wazo_auth_keys.commands'), version='1.0.0', ) self._token = None self._client = None def build_option_parser(self, *args, **kwargs): parser = super().build_option_parser(*args, **kwargs) parser.add_argument( '--wazo-auth-cli-config', default=os.getenv('WAZO_AUTH_CLI_CONFIG', '/root/.config/wazo-auth-cli'), help= 'Extra configuration directory to override the wazo-auth-cli configuration', ) parser.add_argument( '--base-dir', default='/var/lib/wazo-auth-keys', help='The base directory of the file keys', ) parser.add_argument( '--config', default='/etc/wazo-auth-keys/config.yml', help='The wazo-auth-keys configuration file', ) return parser @property def client(self): if not self._client: self._client = Client(**self._auth_config) if not self._token: self._token = self._client.token.new('wazo_user', expiration=600)['token'] self._client.set_token(self._token) return self._client def initialize_app(self, argv): self.LOG.debug('wazo-auth-keys') self.LOG.debug('options=%s', self.options) conf = config.build(self.options) self.LOG.debug('Starting with config: %s', conf) self.LOG.debug('client args: %s', conf['auth']) self._auth_config = dict(conf['auth']) with open(self.options.config, 'r') as f: self.services = yaml.safe_load(f) self.file_manager = FileManager(self, self.options.base_dir)
def main(): args = parse_args() verify_certificate = _extract_verify_certificate(args.verify_certificate) password = os.getenv('PORTAL_PASSWORD', None) if not password: password = getpass() kwargs = {} if args.timeout: kwargs['timeout'] = args.timeout auth_client = AuthClient(args.host, port=443, prefix='/api/auth', verify_certificate=verify_certificate, username=args.username, password=password, **kwargs) token = auth_client.token.new()['token'] auth_client.set_token(token) confd_client = ConfdClient(args.host, port=443, prefix='/api/confd', verify_certificate=verify_certificate, token=token, **kwargs) rcl = [] if args.rcl: rcl = detect_desync_rcl(auth_client, confd_client) users = [] if args.users: users = detect_desync_users(auth_client, confd_client) if args.delete_orphan_users: delete_desync_users(confd_client, users) if not rcl and not users: sys.exit(0) if args.output: output_file = open(args.output, "w") else: output_file = sys.stdout fieldnames = ['uuid', 'type', 'name'] writer = csv.DictWriter(output_file, fieldnames=fieldnames) writer.writeheader() for row in rcl + users: writer.writerow(row) if args.output: output_file.close() sys.exit(1)
def get_auth(cls, config): auth_config = dict(config['auth']) # FIXME(sileht): Keep the certificate auth_config['verify_certificate'] = False auth = AuthClient(**auth_config) token = auth.token.new('wazo_user', expiration=3600) auth.set_token(token["token"]) auth.username = None auth.password = None return auth
def _auto_create_config(): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=36000)['token'] auth_client.set_token(token) confd_client = ConfdClient(token=token, **config['confd']) tenants = auth_client.tenants.list()['items'] base_url = 'https://{host}:{port}/{version}'.format(**config['dird']) profiles_url = '{}/profiles'.format(base_url) conference_url = '{}/backends/conference/sources'.format(base_url) headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Auth-Token': token, } for tenant in tenants: name = tenant['name'] if name == 'master': continue headers['Wazo-Tenant'] = tenant['uuid'] conference_body = dict(name='auto_conference_{}'.format(name), **CONFERENCE_SOURCE_BODY) conference = requests.post( conference_url, headers=headers, json=conference_body, verify=config['dird']['verify_certificate'], ).json() response = requests.get( profiles_url, headers={ 'Accept': 'application/json', 'X-Auth-Token': token, 'Wazo-Tenant': tenant['uuid'], }, verify=config['dird']['verify_certificate'], ).json() for profile in response['items']: profile['services']['lookup']['sources'].append(conference) profile['services']['favorites']['sources'].append(conference) requests.put( '{}/{}'.format(profiles_url, profile['uuid']), json=profile, headers=headers, verify=config['dird']['verify_certificate'], )
def _get_tenants_infos(tenants): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=36000)['token'] auth_client.set_token(token) tenant_infos = {} for tenant in tenants: try: tenant_infos[tenant] = auth_client.tenants.get(tenant) except requests.HTTPError as e: print('Error while getting tenant {}: {}'.format(tenant, e)) return tenant_infos
def _import_wazo_user(users): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new(expiration=36000)['token'] auth_client.set_token(token) with closing(psycopg2.connect(config['db_uri'])) as conn: cursor = conn.cursor() entity_to_tenant_map = _build_entity_tenant_map(cursor) print('migrating users to wazo-auth', end='', flush=True) for user in users: _create_user(auth_client, entity_to_tenant_map, user) print('.', end='', flush=True) print('\ndone') auth_client.token.revoke(token)
def _import_wazo_user(users): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=36000)['token'] auth_client.set_token(token) with closing(psycopg2.connect(config['db_uri'])) as conn: cursor = conn.cursor() tenant_uuid = _find_older_tenant_uuid(cursor) print('migrating services access to wazo-auth', end='', flush=True) for user in users: _create_user(auth_client, tenant_uuid, user) print('.', end='', flush=True) print('\ndone') auth_client.token.revoke(token)
def get_visible_tenants(self, tenant): token = request.headers['X-Auth-Token'] auth_client = AuthClient(**self.auth_config) auth_client.set_token(token) try: visible_tenants = auth_client.tenants.list( tenant_uuid=tenant)['items'] except HTTPError as e: response = getattr(e, 'response', None) status_code = getattr(response, 'status_code', None) if status_code == 401: logger.warning( 'a user is doing multi-tenant queries without the tenant list ACL' ) return [tenant] raise return [tenant['uuid'] for tenant in visible_tenants]
def test_csv_import(): auth_client = AuthClient(constants.HOST, verify_certificate=False, username='******', password='******') dird_client = DirdClient(constants.HOST, https=True, verify_certificate=False, timeout=MAX_TIME) token_data = auth_client.token.new(expiration=300) token = token_data['token'] auth_client.set_token(token) try: auth_client.users.new( username=USERNAME, password=PASSWORD, tenant_uuid=token_data['metadata']['tenant_uuid'], ) except requests.HTTPError as e: if e.response.status_code == 409: pass else: raise user_auth_client = AuthClient( constants.HOST, verify_certificate=False, username=USERNAME, password=PASSWORD, ) token = user_auth_client.token.new('wazo_user', expiration=300)['token'] result, time_to_complete = upload_csv(dird_client, token) assert 'created' in result, 'The result does not contain created contacts' assert len(result['created'] ) == 1000, 'expected 1000 created contacts: {}'.format( len(result['created'])) assert time_to_complete < MAX_TIME, 'The import took too long {}s > {}s'.format( time_to_complete, MAX_TIME)
def migrate_new_status(): config = _load_config() _wait_for_provd(config['provd']) auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=5 * 60) auth_client.set_token(token['token']) master_tenant_uuid = token['metadata']['tenant_uuid'] # Migrate devices for dir_entry in os.scandir(PROVD_JSONDB_DEVICES_DIR): device_id = dir_entry.name try: _migrate_device(device_id, master_tenant_uuid) except json.JSONDecodeError: print(device_id, 'is not a valid JSON file. Skipping.') continue subprocess.run(['systemctl', 'restart', 'xivo-provd'])
def migrate_tenants(): config = _load_config() _wait_for_provd(config['provd']) auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=5*60) auth_client.set_token(token['token']) confd = ConfdClient(token=token['token'], **config['confd']) master_tenant_uuid = token['metadata']['tenant_uuid'] # Migrate associated devices devices_migrated = [] lines = confd.lines.list(recurse=True)['items'] for line in lines: device_id = line['device_id'] if device_id and device_id not in devices_migrated: try: _migrate_device(device_id, line['tenant_uuid']) except json.JSONDecodeError: print(device_id, 'is not a valid JSON file. Skipping.') continue except IOError as e: print('Skipping device "{}": {}'.format(device_id, e)) continue devices_migrated.append(device_id) # Migrate autoprov devices for dir_entry in os.scandir(PROVD_JSONDB_DEVICES_DIR): device_id = dir_entry.name if device_id not in devices_migrated: try: _migrate_device(device_id, master_tenant_uuid) except json.JSONDecodeError: print(device_id, 'is not a valid JSON file. Skipping.') continue subprocess.run(['systemctl', 'restart', 'xivo-provd'])
def main(): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=5 * 60) auth_client.set_token(token['token']) master_tenant_uuid = token['metadata']['tenant_uuid'] all_tenants = [ tenant['uuid'] for tenant in auth_client.tenants.list()['items'] ] for dir_entry in os.scandir(PROVD_JSONDB_DEVICES_DIR): device_id = dir_entry.name try: _change_device_tenant_if_orphan(device_id, master_tenant_uuid, all_tenants) except json.JSONDecodeError: print(device_id, 'is not a valid JSON file. Skipping.') continue subprocess.run(['systemctl', 'restart', 'wazo-provd'])
class TestInit(BaseTestCase): asset = 'base' def setUp(self): super().setUp() self.docker_exec(['wazo-auth-bootstrap']) self.key = self.docker_exec(['cat', '/var/lib/wazo-auth/init.key' ]).decode('utf-8') self.client = Client(self.auth_host, port=self.auth_port, verify_certificate=False) def test_post(self): body = {'username': '******', 'password': '******', 'key': INVALID_KEY} assert_http_error(401, self.client.init.run, **body) assert_http_error(400, self.client.init.run, **copy_without(body, 'username')) assert_http_error(400, self.client.init.run, **copy_without(body, 'password')) assert_http_error(400, self.client.init.run, **copy_without(body, 'key')) result = self.client.init.run(username='******', password='******', key=self.key) assert_that(result, has_entries(uuid=ANY_UUID, emails=empty(), username='******')) token_data = self._post_token('foo', 'bar', expiration=10) self.client.set_token(token_data['token']) user_tenants = self.client.users.get_tenants(result['uuid']) assert_that( user_tenants, has_entries(items=contains(self.get_top_tenant()), total=1)) def get_top_tenant(self): return self.client.tenants.list(name='master')['items'][0]
class WazoAsteriskCLI(App): DEFAULT_VERBOSE_LEVEL = 0 def __init__(self): super().__init__( description='A CLI for the asterisk service', command_manager=CommandManager('wazo_asterisk_cli.commands'), version='0.0.1', ) self._current_token = None self._remove_token = False self._auth_client = None self._client = None self._backend = None def build_option_parser(self, *args, **kwargs): parser = super(WazoAsteriskCLI, self).build_option_parser(*args, **kwargs) config_path_from_env = os.getenv('WAZO_ASTERISK_CLI_CONFIG', None) config_path_default = os.path.expanduser( os.path.join('~', '.config', 'wazo-asterisk-cli')) parser.add_argument( '--config', default=(config_path_from_env or config_path_default), help= 'Extra configuration directory to override the system configuration', ) parser.add_argument('--hostname', help='The wazo-auth hostname') parser.add_argument('--port', help='The wazo-auth port') https_verification = parser.add_mutually_exclusive_group() https_verification.add_argument('--ssl', help="Use ssl") https_verification.add_argument('--no-ssl', help="Don't use ssl") https_verification.add_argument( '--verify', action='store_true', help='Verify the HTTPS certificate or not') https_verification.add_argument('--insecure', action='store_true', help='Bypass certificate verification') https_verification.add_argument('--cacert', help='Specify the ca bundle file') auth_or_token = parser.add_mutually_exclusive_group() auth_or_token.add_argument('--token', help='The wazo-auth token to use') username_password = auth_or_token.add_argument_group() username_password.add_argument( '--auth-username', metavar='auth_username', help='The username to use to retrieve a token', ) username_password.add_argument( '--auth-password', metavar='auth_password', help='The password to use to retrieve a token', ) username_password.add_argument( '--backend', help='The backend to use when authenticating') return parser @property def client(self): if not self._auth_client: self._auth_client = Auth(**self._auth_config) if not self._current_token: self._backend = self._auth_config.pop('backend', None) or self._backend self._auth_client = Auth(**self._auth_config) args = {'expiration': 3600} if self._backend: args['backend'] = self._backend token_data = self._auth_client.token.new(**args) self._current_token = token_data['token'] self._auth_client.set_token(self._current_token) self._client = Amid(**self._amid_config) self._client.set_token(self._current_token) return self._client @property def client_without_token(self): if not self._client: self._client = Auth(**self._auth_config) return self._client def initialize_app(self, argv): self.LOG.debug('Wazo Asterisk CLI') self.LOG.debug('options=%s', self.options) conf = config.build(self.options) self.LOG.debug('Starting with config: %s', conf) self._current_token = self.options.token self.LOG.debug('client args: %s', conf['auth']) self._auth_config = dict(conf['auth']) self._amid_config = dict(conf['amid']) def clean_up(self, cmd, result, err): if err: self.LOG.debug('got an error: %s', err) if self._remove_token: self._auth_client.token.revoke(self._current_token) self._remove_token = False
def _auto_create_config(): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=36000)['token'] auth_client.set_token(token) tenants = auth_client.tenants.list()['items'] base_url = 'https://{host}:{port}/{version}'.format( **config['dird'] ) conference_url = '{}/backends/conference/sources'.format(base_url) profiles_url = '{}/profiles'.format(base_url) displays_url = '{}/displays'.format(base_url) personal_url = '{}/backends/personal/sources'.format(base_url) wazo_url = '{}/backends/wazo/sources'.format(base_url) office365_url = '{}/backends/office365/sources'.format(base_url) sources_url = '{}/sources'.format(base_url) headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Auth-Token': token, } for tenant in tenants: name = tenant['name'] if name == 'master': continue headers['Wazo-Tenant'] = tenant['uuid'] def post(*args, **kwargs): return requests.post( *args, headers=headers, verify=config['dird']['verify_certificate'], **kwargs ) post(conference_url, json=CONFERENCE_SOURCE_BODY) post(personal_url, json=PERSONAL_SOURCE_BODY) wazo_body = dict(name='auto_wazo_{}'.format(name), **WAZO_SOURCE_BODY) post(wazo_url, json=wazo_body) # add office365 is the plugin is installed office365_body = dict(name='auto_office365_{}'.format(name), **OFFICE_365_SOURCE_BODY) post(office365_url, json=office365_body) display_name = 'auto_{}'.format(name) display_body = { 'name': display_name, 'columns': DEFAULT_DISPLAY_COLUMNS, } post(displays_url, json=display_body).json() # GET all auto created sources before or during this migration response = requests.get( sources_url, headers=headers, verify=config['dird']['verify_certificate'], ) sources = [] if response.status_code == 200: for source in response.json()['items']: if source['name'] == 'personal': sources.append(source) continue if not source['name'].startswith('auto_'): continue if not source['name'].endswith(name): continue sources.append(source) # GET the display because the POST could return a 409 if it already exists response = requests.get( displays_url, headers=headers, verify=config['dird']['verify_certificate'], ) display = None if response.status_code == 200: for d in response.json()['items']: if d['name'] == display_name: display = d break profile_body = { 'name': 'default', 'display': display, 'services': { 'lookup': {'sources': sources}, 'favorites': {'sources': sources}, 'reverse': {'sources': sources, 'timeout': 0.5}, }, } post(profiles_url, json=profile_body)
def _auto_create_config(): config = _load_config() auth_client = AuthClient(**config['auth']) token = auth_client.token.new('wazo_user', expiration=36000)['token'] auth_client.set_token(token) confd_client = ConfdClient(token=token, **config['confd']) tenants = auth_client.tenants.list()['items'] base_url = 'https://{host}:{port}/{version}'.format(**config['dird']) profiles_url = '{}/profiles'.format(base_url) displays_url = '{}/displays'.format(base_url) personal_url = '{}/backends/personal/sources'.format(base_url) wazo_url = '{}/backends/wazo/sources'.format(base_url) office365_url = '{}/backends/office365/sources'.format(base_url) headers = { 'Accept': 'application/json', 'Content-Type': 'application/json', 'X-Auth-Token': token, } for tenant in tenants: name = tenant['name'] if name == 'master': continue headers['Wazo-Tenant'] = tenant['uuid'] def post(*args, **kwargs): return requests.post(*args, headers=headers, verify=config['dird']['verify_certificate'], **kwargs) personal_body = PERSONAL_SOURCE_BODY personal = post(personal_url, json=personal_body) if personal.status_code == 409: # Default configuration already exists return wazo_body = dict(name='auto_wazo_{}'.format(name), **WAZO_SOURCE_BODY) wazo = post(wazo_url, json=wazo_body) sources = [personal.json(), wazo.json()] # add office365 is the plugin is installed office365_body = dict(name='auto_office365_{}'.format(name), **OFFICE_365_SOURCE_BODY) office365 = post(office365_url, json=office365_body) if office365.status_code == 201: sources.append(office365) display_body = { 'name': 'auto_{}'.format(name), 'columns': DEFAULT_DISPLAY_COLUMNS, } display = post(displays_url, json=display_body).json() contexts = confd_client.contexts.list( tenant_uuid=tenant['uuid'])['items'] for context in contexts: if context['type'] != 'internal': continue profile_body = { 'name': context['name'], 'display': display, 'services': { 'lookup': { 'sources': sources }, 'favorites': { 'sources': sources }, 'reverse': { 'sources': sources, 'timeout': 0.5 }, }, } post(profiles_url, json=profile_body)
def from_config(cls, *args, **kwargs): client = AuthClient(*args, **kwargs) # 30 minutes should be enought to import all users token = client.token.new(expiration=30 * 30)['token'] client.set_token(token) return cls(client)