def visit_let_node(self, node: LetNode): for kv_node in node.assignments_node.kv_nodes: lhs_chain = [] with self.consumer(lambda v: lhs_chain.append(v)): self._unwind_lhs(kv_node.key_node) rhs = Ref() with self.consumer(lambda v: rhs.set(v)): kv_node.value_node.accept(self) if len(lhs_chain) == 1: self.context[lhs_chain[0]] = rhs.get() else: if not lhs_chain[0] in self.context: raise PeekError(f'Unknown name: {lhs_chain[0]!r}') lhs = self.context[lhs_chain[0]] for x in lhs_chain[1:-1]: if isinstance(lhs, dict) or (isinstance(lhs, list) and isinstance(x, int)): lhs = lhs[x] else: raise PeekError( f'Invalid lhs for assignment: {lhs_chain}') x = lhs_chain[-1] if isinstance(lhs, dict) or (isinstance(lhs, list) and isinstance(x, int)): lhs[x] = rhs.get() else: raise PeekError(f'Invalid lhs for assignment: {lhs_chain}')
def current(self): if self._index_current is None: raise PeekError('No ES client is configured') if self._index_current < 0 or self._index_current >= len( self._clients): raise PeekError( f'Attempt to get ES client at invalid index [{self._index_current}]' ) return self._clients[self._index_current]
def move_current_to(self, idx: int): if not isinstance(idx, int): raise PeekError(f'Index must be integer, got {idx!r}') if 0 <= idx < len(self._clients): if idx == self._index_current: return self._clients.insert(idx, self._clients.pop(self._index_current)) self._index_current = idx else: raise PeekError( f'Attempt to move ES client to an invalid index: {idx!r}')
def _connect_userpass(app, **options): username = options.get('username') password = options.get('password') if options['hosts']: service_name = f'peek/{options["hosts"]}/userpass' else: service_name = f'peek/{options["cloud_id"]}/userpass' if not username and password: raise PeekError('Username is required for userpass authentication') if options['force_prompt']: password = app.input(message='Please enter password: '******'PEEK_PASSWORD', None) if not password: if app.config.as_bool('use_keyring'): password = _keyring(service_name, username) if not password: if options['no_prompt']: raise PeekError( 'Password is not found and password prompt is disabled' ) password = app.input(message='Please enter password: '******'no_prompt']: raise PeekError( 'Password is not found and password prompt is disabled' ) password = app.input(message='Please enter password: '******'use_keyring'): _keyring(service_name, username, password) return EsClient( name=options['name'], hosts=options['hosts'], cloud_id=options['cloud_id'], username=username, password=password, use_ssl=options['use_ssl'], verify_certs=options['verify_certs'], assert_hostname=options['assert_hostname'], ca_certs=options['ca_certs'], client_cert=options['client_cert'], client_key=options['client_key'], headers=options['headers'], )
def _unwind_lhs(self, node: Node): if isinstance(node, NameNode): self.consume(node.token.value) elif isinstance(node, BinOpNode): if node.op_token.value != '.': raise PeekError( f'lhs can only have variable and dot notation, but got {node!r}' ) self._unwind_lhs(node.left_node) node.right_node.accept(self) else: raise PeekError( f'lhs can only have variable and dot notation, but got {node!r}' )
def saml_authenticate(es_client: EsClient, realm: str, callback_port: str, callback_ssl: bool, name: Optional[str]): _logger.info( f'SAML authenticate for realm {realm!r} and callback port {callback_port!r}' ) prepare_response = _saml_prepare(es_client, realm) httpd = _saml_start_http_server(callback_port, callback_ssl) print('Please use browser to complete authentication against the idP') webbrowser.open(prepare_response['redirect']) callback_path = _SamlExchange.callback_path.get() if isinstance(callback_path, bytes): callback_path = callback_path.decode('utf-8') try: httpd.shutdown() query = urllib.parse.parse_qs(callback_path) if 'SAMLResponse' not in query or len(query['SAMLResponse']) != 1: raise PeekError( f'Invalid saml callback response: {callback_path!r}') content = query['SAMLResponse'][0] auth_response = _saml_do_authenticate(es_client, realm, prepare_response['id'], content) return RefreshingEsClient(es_client, auth_response['username'], auth_response['access_token'], auth_response['refresh_token'], auth_response['expires_in'], name=name) finally: _SamlExchange.callback_path = None
def krb_authenticate(es_client: EsClient, service=None, username=None, name=None): _logger.debug(f'Connection with Kerberos: {service}') if service is None: raise PeekError('Service is required for kerberos authentication') import kerberos result, context = kerberos.authGSSClientInit(service, principal=username) kerberos.authGSSClientStep(context, '') ticket = kerberos.authGSSClientResponse(context) _logger.debug(f'Kerberos ticket: {ticket}') auth_response = es_client.perform_request( 'POST', '/_security/oauth2/token', json.dumps({ 'grant_type': '_kerberos', 'kerberos_ticket': ticket, }), deserialize_it=True) _logger.debug(f'Kerberos Token auth response: {auth_response}') return RefreshingEsClient( es_client, '_KRB', auth_response['access_token'], auth_response['refresh_token'], auth_response['expires_in'], name=name)
def remove_client(self, x=None): if x is None: self.remove_client(self._index_current) elif isinstance(x, str): idx = self._clients.index(self.get_client(x)) self.remove_client(idx) elif isinstance(x, int): if x < 0 or x >= len(self._clients): raise PeekError( f'Attempt to remove ES client at invalid index [{x}]') removed = self._clients.pop(x) if not self._clients: self._index_current = None return if x < self._index_current: self._index_current -= 1 elif x == self._index_current: self._index_current = 0 for listener in self.listeners: if listener.on_remove(self, removed) is False: break else: raise ValueError( f'Connection must be specified by either name or index, got {x!r}' )
def dot(left_operand, right_operand): if isinstance(left_operand, dict): if right_operand in left_operand: return left_operand[right_operand] else: raise PeekError( f'Value {left_operand!r} does not have key {right_operand!r}') elif isinstance(left_operand, list): if isinstance(right_operand, int): return left_operand[right_operand] else: raise PeekError( f'Cannot index array {left_operand!r} with non-integer value: {right_operand!r}' ) else: raise PeekError( f'Value {left_operand!r} must be either an array or dict')
def visit_unary_op_node(self, node: UnaryOpNode): operand = Ref() with self.consumer(lambda v: operand.set(v)): node.operand_node.accept(self) op_func = self._unary_op_funcs.get(node.op_token.value, None) if op_func is None: raise PeekError(f'Unknown unary operator: {node.op_token.value!r}') self.consume(op_func(operand.get()))
def start_capture(self, f=None): if not isinstance(self.capture, NoOpCapture): raise PeekError( f'Cannot capture when one is currently running: {self.capture.status()}' ) if f is None: f = f'{datetime.now().strftime("%Y%m%d%H%M%S")}.es' self.capture = FileCapture(f) return self.capture.status()
def consolidate_options(options, defaults): """ Merge shorthanded @symbol into normal options kv pair with provided defaults """ final_options = {k: v for k, v in options.items() if k != '@'} for symbol in options.get('@', []): if symbol not in defaults: raise PeekError(f'Unknown shorthanded flag: {symbol}') final_options[symbol] = defaults[symbol] return final_options
def __call__(self, app, func=None, **options): if func is None: return '\n'.join(k for k in app.vm.functions.keys()) for k, v in app.vm.functions.items(): if v == func: description = getattr(v, "description", None) header = k + (f' - {description}' if description else '') return f'{header}\n{getattr(func, "options", {})}' else: raise PeekError(f'No such function: {func}')
def __call__(self, app, index=None, **options): if index is None: history = [] for entry in app.history.load_recent( size=options.get('size', 100)): history.append(f'{entry[0]:>6} {entry[1]!r}') return '\n'.join(history) else: entry = app.history.get_entry(index) if entry is None: raise PeekError(f'History not found for index: {index}') app.process_input(entry[1])
def __call__(self, app, **options): options = consolidate_options(options, { 'load': DEFAULT_SAVE_NAME, 'save': DEFAULT_SAVE_NAME, 'clear': None, }) if not options: return app.history.list_sessions() if 'load' in options: load = options.get('load') data = app.history.load_session(load) if data is None: raise PeekError(f'Session not found: {load!r}') else: app.es_client_manager = EsClientManager.from_dict( app, json.loads(data)) return str(app.es_client_manager) elif 'save' in options: save = options.get('save') app.history.save_session( save, json.dumps(app.es_client_manager.to_dict())) return f'Session save as: {save!r}' elif 'remove' in options: remove = options.get('remove') if app.history.delete_session(remove): return f'Session removed: {remove!r}' else: raise PeekError(f'Session not found: {remove!r}') elif 'clear' in options: app.history.clear_sessions() return 'All sessions cleared' else: raise PeekError(f'Unknown options: {options}')
def __call__(self, app, f=None, **options): directives = options.get('@') if not directives: return app.capture.status() # Only honor first directive directive = directives[0] if directive == 'start': return app.start_capture(f) elif directive == 'stop': return app.stop_capture() else: raise PeekError(f'Unknown capture directive: {directive}')
def visit_bin_op_node(self, node: BinOpNode): left_operand = Ref() with self.consumer(lambda v: left_operand.set(v)): node.left_node.accept(self) right_operand = Ref() with self.consumer(lambda v: right_operand.set(v)): node.right_node.accept(self) op_func = self._bin_op_funcs.get(node.op_token.value, None) if op_func is None: raise PeekError( f'Unknown binary operator: {node.op_token.value!r}') self.consume(op_func(left_operand.get(), right_operand.get()))
def visit_for_in_node(self, node: ForInNode): var_name = node.item.token.value items_ref = Ref() with self.consumer(lambda v: items_ref.set(v)): node.items.accept(self) items = items_ref.get() if not isinstance(items, list): raise PeekError( f'For in loop must operator over a list, got {items!r}') for i in items: self.context[var_name] = i for n in node.suite: n.accept(self)
def set_current(self, x): if isinstance(x, str): name = x self._index_current = self._clients.index(self.get_client(name)) elif isinstance(x, int): if x < 0 or x >= len(self._clients): raise PeekError( f'Attempt to set ES client at invalid index {x!r}') self._index_current = x else: raise ValueError( f'Connection must be specified by either name or index, got {x!r}' ) for listener in self.listeners: if listener.on_set(self) is False: break
def __call__(self, app, **options): service = options.get('service', None) conn = options.get('conn', None) if service is None: if app.es_client_manager.current.hosts: host = urllib.parse.urlparse(app.es_client_manager.current.hosts.split(',')[0]).hostname service = f'HTTP@{host}' else: raise PeekError('Cannot infer service principal. Please specify explicitly') krb_es_client = krb_authenticate( app.es_client_manager.current if conn is None else app.es_client_manager.get_client(conn), service, options.get('username', None), options.get('name', None) ) app.es_client_manager.add(krb_es_client) return app.es_client_manager.current.perform_request('GET', '/_security/_authenticate')
def __call__(self, app, *args, **options): self.function_lookup = {v: k for k, v in app.vm.functions.items()} content = [] for arg in args: content.append( json.dumps(arg, cls=PeekEncoder, app=app, separators=(',', ':'))) content = ' '.join(content) if 'file' in options: end = '\n' # this does not make sense in interactive mode, so hardcode it for now fname = options.get('file') if not isinstance(fname, str): raise PeekError(f'file name must be string, got {fname!r}') with open(fname, 'a') as outs: outs.write(f'{content}{end}') else: return content
def get_client(self, x=None): if x is None: return self.current elif isinstance(x, str): name = x if not name: raise ValueError('Name cannot be empty') for c in self._clients: if c.name == name: return c else: raise ValueError(f'No client with name: {name!r}') elif isinstance(x, int): if x < 0 or x >= len(self._clients): raise PeekError( f'Attempt to set ES client at invalid index [{x}]') return self._clients[x] else: raise ValueError( f'Connection must be specified by either name or index, got {x!r}' )
def visit_func_call_node(self, node: FuncCallNode): if isinstance(node.name_node, NameNode): func = self.get_value(node.name_node.token.value) else: func_ref = Ref() with self.consumer(lambda v: func_ref.set(v)): node.name_node.accept(self) func = func_ref.get() if not callable(func): raise PeekError( f'{node.name_node!r} is not a callable, but {func!r}') func_symbols = Ref() with self.consumer(lambda v: func_symbols.set(v)): node.symbols_node.accept(self) func_args = Ref() with self.consumer(lambda v: func_args.set(v)): node.args_node.accept(self) for kv_node in node.kwargs_node.kv_nodes: assert isinstance(kv_node.key_node, NameNode), f'{kv_node.key_node!r}' func_kwargs = Ref() with self.consumer(lambda v: func_kwargs.set(v)): self._do_visit_dict_node(node.kwargs_node, resolve_key_name=False) kwargs = func_kwargs.get() if func_symbols.get(): kwargs['@'] = func_symbols.get() try: result = func(self.app, *func_args.get(), **kwargs) if node.is_stmt: self.app.display.info(result) else: self.consume(result) except Exception as e: _logger.exception( f'Error on invoking function: {node.name_node!r}') self.app.display.error(e)