def cloudwatch(): try: incomingAlert = parse_notification(request.data) except ValueError as e: raise ApiError(str(e), 400) incomingAlert.customer = assign_customer(wanted=incomingAlert.customer) add_remote_ip(request, incomingAlert) try: alert = process_alert(incomingAlert) except RejectException as e: raise ApiError(str(e), 403) except Exception as e: raise ApiError(str(e), 500) if alert: return jsonify(status="ok", id=alert.id, alert=alert.serialize), 201 else: raise ApiError("insert or update of cloudwatch alarm failed", 500)
def delete_blackout(blackout_id): customer = g.get('customer', None) blackout = Blackout.find_by_id(blackout_id, customer) if not blackout: raise ApiError('not found', 404) write_audit_trail.send(current_app._get_current_object(), event='blackout-deleted', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=blackout.id, type='blackout', request=request) if blackout.delete(): return jsonify(status='ok') else: raise ApiError('failed to delete blackout', 500)
def delete_heartbeat(heartbeat_id): customer = g.get('customer', None) heartbeat = Heartbeat.find_by_id(heartbeat_id, customer) if not heartbeat: raise ApiError('not found', 404) write_audit_trail.send(current_app._get_current_object(), event='heartbeat-deleted', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=heartbeat.id, type='heartbeat', request=request) if heartbeat.delete(): return jsonify(status='ok') else: raise ApiError('failed to delete heartbeat', 500)
def get_group_users(group_id): if not Group.find_by_id(group_id): raise ApiError('not found', 404) group_users = GroupUsers.find_by_id(group_id) if group_users: return jsonify(status='ok', users=[user.serialize for user in group_users], total=len(group_users)) else: return jsonify(status='ok', users=[], total=0)
def delete_alert(alert_id): customers = g.get('customers', None) alert = Alert.find_by_id(alert_id, customers) if not alert: raise ApiError('not found', 404) write_audit_trail.send(current_app._get_current_object(), event='alert-deleted', message='', user=g.user, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) if alert.delete(): return jsonify(status='ok') else: raise ApiError('failed to delete alert', 500)
def add_note(alert_id): note_text = request.json.get('text') or request.json.get('note') if not note_text: raise ApiError("must supply 'note' text") customers = g.get('customers', None) alert = Alert.find_by_id(alert_id, customers) if not alert: raise ApiError('not found', 404) note = alert.add_note(note_text) write_audit_trail.send(current_app._get_current_object(), event='alert-note-added', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=note.id, type='note', request=request) if note: return jsonify(status='ok', id=note.id, note=note.serialize), 201, {'Location': absolute_url('/alert/{}/note/{}'.format(alert.id, note.id))} else: raise ApiError('failed to add note for alert', 500)
def add_note(alert_id): note_text = request.json.get('text') or request.json.get('note') if not note_text: raise ApiError("must supply 'note' text", 400) customers = g.get('customers', None) alert = Alert.find_by_id(alert_id, customers) if not alert: raise ApiError('not found', 404) try: alert, note_text = process_note(alert, note_text) note = alert.add_note(note_text) except RejectException as e: write_audit_trail.send(current_app._get_current_object(), event='alert-note-rejected', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=note.id, type='note', request=request) raise ApiError(str(e), 400) except ForwardingLoop as e: return jsonify(status='ok', message=str(e)), 202 except AlertaException as e: raise ApiError(e.message, code=e.code, errors=e.errors) except Exception as e: raise ApiError(str(e), 500) write_audit_trail.send(current_app._get_current_object(), event='alert-note-added', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=note.id, type='note', request=request) if note: return jsonify(status='ok', id=note.id, note=note.serialize), 201, {'Location': absolute_url('/alert/{}/note/{}'.format(alert.id, note.id))} else: raise ApiError('failed to add note for alert', 500)
def pagerduty(): data = request.json updated = False if data and 'messages' in data: for message in data['messages']: try: incident_key, status, text = parse_pagerduty(message) except ValueError as e: raise ApiError(str(e), 400) customer = g.get('customer', None) try: alert = Alert.get(id=incident_key, customer=customer) except Exception as e: raise ApiError(str(e), 500) if not alert: raise ApiError("not found", 404) try: updated = alert.set_status(status, text) except Exception as e: raise ApiError(str(e), 500) else: raise ApiError("no messages in PagerDuty data payload", 400) if updated: return jsonify(status="ok"), 200 else: raise ApiError("update PagerDuty incident status failed", 500)
def receive(): try: alert = Alert.parse(request.json) except ValueError as e: raise ApiError(str(e), 400) alert.customer = assign_customer(wanted=alert.customer) def audit_trail_alert(event: str): write_audit_trail.send(current_app._get_current_object(), event=event, message=alert.text, user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) try: alert = process_alert(alert) except RejectException as e: audit_trail_alert(event='alert-rejected') raise ApiError(str(e), 403) except RateLimit as e: audit_trail_alert(event='alert-rate-limited') return jsonify(status='error', message=str(e), id=alert.id), 429 except HeartbeatReceived as heartbeat: audit_trail_alert(event='alert-heartbeat') return jsonify(status='ok', message=str(heartbeat), id=heartbeat.id), 202 except BlackoutPeriod as e: audit_trail_alert(event='alert-blackout') return jsonify(status='ok', message=str(e), id=alert.id), 202 except ForwardingLoop as e: return jsonify(status='ok', message=str(e)), 202 except AlertaException as e: raise ApiError(e.message, code=e.code, errors=e.errors) except Exception as e: raise ApiError(str(e), 500) write_audit_trail.send(current_app._get_current_object(), event='alert-received', message=alert.text, user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) if alert: return jsonify(status='ok', id=alert.id, alert=alert.serialize), 201 else: raise ApiError('insert or update of received alert failed', 500)
def update_me(): if not request.json: raise ApiError('nothing to change', 400) if 'roles' in request.json: raise ApiError('not allowed to update roles', 400) if 'email_verified' in request.json: raise ApiError('not allowed to set email verified', 400) user = User.find_by_id(g.user_id) if not user: raise ApiError('not found', 404) if 'email' in request.json: user_by_email = User.find_by_email(email=request.json['email']) if user_by_email and user_by_email.id != user.id: raise ApiError('user with that email already exists', 409) updated = user.update(**request.json) write_audit_trail.send(current_app._get_current_object(), event='user-me-updated', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=user.id, type='user', request=request) if updated: return jsonify(status='ok', user=updated.serialize) else: raise ApiError('failed to update user', 500)
def set_status(alert_id): status = request.json.get('status', None) text = request.json.get('text', '') timeout = request.json.get('timeout', None) if not status: raise ApiError("must supply 'status' as json data", 400) customers = g.get('customers', None) alert = Alert.find_by_id(alert_id, customers) if not alert: raise ApiError('not found', 404) try: alert, status, text = process_status(alert, status, text) alert = alert.from_status(status, text, timeout) except RejectException as e: write_audit_trail.send(current_app._get_current_object(), event='alert-status-rejected', message=alert.text, user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) raise ApiError(str(e), 400) except AlertaException as e: raise ApiError(e.message, code=e.code, errors=e.errors) except Exception as e: raise ApiError(str(e), 500) write_audit_trail.send(current_app._get_current_object(), event='alert-status-changed', message=text, user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) if alert: return jsonify(status='ok') else: raise ApiError('failed to set status', 500)
def action_alert(alert_id): action = request.json.get('action', None) text = request.json.get('text', '%s operator action' % action) timeout = request.json.get('timeout', None) if not action: raise ApiError("must supply 'action' as json data", 400) customers = g.get('customers', None) alert = Alert.find_by_id(alert_id, customers) if not alert: raise ApiError('not found', 404) try: alert, action, text, timeout = process_action(alert, action, text, timeout) alert = alert.from_action(action, text, timeout) except RejectException as e: write_audit_trail.send(current_app._get_current_object(), event='alert-action-rejected', message=alert.text, user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) raise ApiError(str(e), 400) except InvalidAction as e: raise ApiError(str(e), 409) except Exception as e: raise ApiError(str(e), 500) write_audit_trail.send(current_app._get_current_object(), event='alert-actioned', message=text, user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) if alert: return jsonify(status='ok') else: raise ApiError('failed to action alert', 500)
def delete_alert(alert_id): customers = g.get('customers', None) alert = Alert.find_by_id(alert_id, customers) if not alert: raise ApiError('not found', 404) try: deleted = process_delete(alert) except RejectException as e: write_audit_trail.send(current_app._get_current_object(), event='alert-delete-rejected', message=alert.text, user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) raise ApiError(str(e), 400) except AlertaException as e: raise ApiError(e.message, code=e.code, errors=e.errors) except Exception as e: raise ApiError(str(e), 500) write_audit_trail.send(current_app._get_current_object(), event='alert-deleted', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=alert.id, type='alert', request=request) if deleted: return jsonify(status='ok') else: raise ApiError('failed to delete alert', 500)
def stackdriver(): try: incomingAlert = parse_stackdriver(request.get_json(force=True)) except ValueError as e: raise ApiError(str(e), 400) incomingAlert.customer = assign_customer(wanted=incomingAlert.customer) add_remote_ip(request, incomingAlert) try: alert = process_alert(incomingAlert) except RejectException as e: raise ApiError(str(e), 403) except Exception as e: raise ApiError(str(e), 500) if alert: return jsonify(status='ok', id=alert.id, alert=alert.serialize), 201 else: raise ApiError('insert or update of StackDriver notification failed', 500)
def get_user_groups(user_id): user = User.find_by_id(user_id) if not user: raise ApiError('not found', 404) user_groups = user.get_groups() if user_groups: return jsonify(status='ok', groups=[group.serialize for group in user_groups], total=len(user_groups)) else: return jsonify(status='ok', message='not found', groups=[], total=0)
def login(): # lookup user from username/email try: username = request.json.get('username', None) or request.json['email'] password = request.json['password'] except KeyError: raise ApiError("must supply 'username' and 'password'", 401) user = User.check_credentials(username, password) if not user: raise ApiError('Invalid username or password', 401) # if email verification is enforced, deny login and send email if current_app.config['EMAIL_VERIFICATION'] and not user.email_verified: user.send_confirmation() raise ApiError('email not verified', 403) # check allowed domain if not_authorized('ALLOWED_EMAIL_DOMAINS', groups=[user.domain]): raise ApiError('unauthorized domain', 403) # check user is active & update last login if user.status != 'active': raise ApiError('User {} not active'.format(user.email), 403) user.update_last_login() groups = [g.name for g in user.get_groups()] scopes = Permission.lookup(login=user.email, roles=user.roles + groups) customers = get_customers(login=user.email, groups=[user.domain] + groups) auth_audit_trail.send(current_app._get_current_object(), event='basic-auth-login', message='user login via BasicAuth', user=user.email, customers=customers, scopes=scopes, resource_id=user.id, type='user', request=request) # generate token token = create_token(user_id=user.id, name=user.name, login=user.email, provider='basic', customers=customers, scopes=scopes, roles=user.roles, groups=groups, email=user.email, email_verified=user.email_verified) return jsonify(token=token.tokenize)
def create_blackout(): try: blackout = Blackout.parse(request.json) except Exception as e: raise ApiError(str(e), 400) if g.get('customer', None): blackout.customer = g.get('customer') try: blackout = blackout.create() except Exception as e: raise ApiError(str(e), 500) if blackout: return jsonify(status="ok", id=blackout.id, blackout=blackout.serialize), 201, { 'Location': absolute_url('/blackout/' + blackout.id) } else: raise ApiError("insert blackout failed", 500)
def get_key(key): if not current_app.config['AUTH_REQUIRED']: key = ApiKey.find_by_id(key) elif Scope.admin in g.scopes or Scope.admin_keys in g.scopes: key = ApiKey.find_by_id(key) else: user = g.get('login', None) key = ApiKey.find_by_id(key, user) if key: return jsonify(status='ok', total=1, key=key.serialize) else: raise ApiError('not found', 404)
def process_blackout(blackout, action : str) -> None: wanted_plugins, wanted_config = plugins.routing(None) for plugin in wanted_plugins: try: plugin.blackout_change(blackout, action) except NotImplementedError: pass except Exception as e: if current_app.config['PLUGINS_RAISE_ON_ERROR']: raise ApiError("Error while running blackout-chanded plugin '{}': {}".format(plugin.name, str(e))) else: logging.error("Error while running blackout-changed plugin '{}': {}".format(plugin.name, str(e)))
def custom(webhook): try: incomingAlert = custom_webhooks.webhooks[webhook].incoming( query_string=request.args, payload=request.get_json() or request.get_data(as_text=True)) except ValueError as e: raise ApiError(str(e), 400) incomingAlert.customer = assign_customer(wanted=incomingAlert.customer) add_remote_ip(request, incomingAlert) try: alert = process_alert(incomingAlert) except RejectException as e: raise ApiError(str(e), 403) except Exception as e: raise ApiError(str(e), 500) if alert: return jsonify(status="ok", id=alert.id, alert=alert.serialize), 201 else: raise ApiError("insert or update via %s webhook failed" % webhook, 500)
def get_oidc_configuration(app): OIDC_ISSUER_URL_BY_PROVIDER = { 'azure': 'https://sts.windows.net/{}/'.format(app.config['AZURE_TENANT']), 'gitlab': app.config['GITLAB_URL'], 'google': 'https://accounts.google.com', 'keycloak': '{}/auth/realms/{}'.format(app.config['KEYCLOAK_URL'], app.config['KEYCLOAK_REALM']) } issuer_url = OIDC_ISSUER_URL_BY_PROVIDER.get( app.config['AUTH_PROVIDER']) or app.config['OIDC_ISSUER_URL'] if not issuer_url: raise ApiError( 'Must define Issuer URL (OIDC_ISSUER_URL) in server configuration to use OpenID Connect.', 503) discovery_doc_url = issuer_url.strip( '/') + '/.well-known/openid-configuration' r = requests.get(discovery_doc_url) config = r.json() if config['issuer'] != issuer_url: raise ApiError( 'Issuer Claim does not match Issuer URL used to retrieve OpenID configuration', 503) jwks_uri = config['jwks_uri'] r = requests.get(jwks_uri) keys = { k['kid']: RSAAlgorithm.from_jwk(json.dumps(k)) for k in r.json()['keys'] } return config, keys
def get_oidc_configuration(app): OIDC_ISSUER_URL_BY_PROVIDER = { 'azure': 'https://login.microsoftonline.com/{}/v2.0'.format(app.config['AZURE_TENANT']), 'cognito': 'https://cognito-idp.{}.amazonaws.com/{}'.format(app.config['AWS_REGION'], app.config['COGNITO_USER_POOL_ID']), 'gitlab': app.config['GITLAB_URL'], 'google': 'https://accounts.google.com', 'keycloak': '{}/auth/realms/{}'.format(app.config['KEYCLOAK_URL'], app.config['KEYCLOAK_REALM']) } issuer_url = OIDC_ISSUER_URL_BY_PROVIDER.get(app.config['AUTH_PROVIDER']) or app.config['OIDC_ISSUER_URL'] if not issuer_url: raise ApiError('Must define Issuer URL (OIDC_ISSUER_URL) in server configuration to use OpenID Connect.', 503) discovery_doc_url = issuer_url.strip('/') + '/.well-known/openid-configuration' try: r = requests.get(discovery_doc_url, timeout=2) config = r.json() except Exception as e: raise ApiError('Could not get OpenID configuration from well known URL: {}'.format(str(e)), 503) if 'issuer' not in config: error = config.get('error') or config.get('message') or config raise ApiError('OpenID Connect issuer response invalid: {}'.format(error)) if config['issuer'].format(tenantid=app.config['AZURE_TENANT']) != issuer_url: raise ApiError('Issuer Claim does not match Issuer URL used to retrieve OpenID configuration', 503) if app.config['OIDC_VERIFY_TOKEN']: try: jwks_uri = config['jwks_uri'] r = requests.get(jwks_uri, timeout=2) keys = {k['kid']: RSAAlgorithm.from_jwk(json.dumps(k)) for k in r.json()['keys']} except Exception as e: raise ApiError('Could not get OpenID JWT Key Set from JWKS URL: {}'.format(str(e)), 503) else: keys = {} return config, keys
def update_key(key): if not request.json: raise ApiError('nothing to change', 400) if not current_app.config['AUTH_REQUIRED']: key = ApiKey.find_by_id(key) elif Scope.admin in g.scopes or Scope.admin_keys in g.scopes: key = ApiKey.find_by_id(key) else: key = ApiKey.find_by_id(key, user=g.login) if not key: raise ApiError('not found', 404) update = request.json update['customer'] = assign_customer(wanted=update.get('customer'), permission=Scope.admin_keys) for want_scope in update.get('scopes', []): if not Permission.is_in_scope(want_scope, have_scopes=g.scopes): raise ApiError( "Requested scope '{}' not in existing scopes: {}".format( want_scope, ','.join(g.scopes)), 403) admin_audit_trail.send(current_app._get_current_object(), event='apikey-updated', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=key.id, type='apikey', request=request) updated = key.update(**request.json) if updated: return jsonify(status='ok', key=updated.serialize) else: raise ApiError('failed to update API key', 500)
def create_blackout(): try: blackout = Blackout.parse(request.json) except Exception as e: raise ApiError(str(e), 400) blackout.customer = assign_customer(wanted=blackout.customer, permission='admin:blackouts') try: blackout = blackout.create() except Exception as e: raise ApiError(str(e), 500) if blackout: return jsonify(status="ok", id=blackout.id, blackout=blackout.serialize), 201, { 'Location': absolute_url('/blackout/' + blackout.id) } else: raise ApiError("insert blackout failed", 500)
def bulk_action_alert(): from alerta.tasks import action_alerts action = request.json.get('action', None) text = request.json.get('text', 'bulk status update') timeout = request.json.get('timeout', None) if not action: raise ApiError("must supply 'action' as json data", 400) query = qb.from_params(request.args) alerts = [alert.id for alert in Alert.find_all(query)] if not alerts: raise ApiError('not found', 404) task = action_alerts.delay(alerts, action, text, timeout) return jsonify(status='ok', message=f'{len(alerts)} alerts queued for action'), 202, { 'Location': absolute_url('/_bulk/task/' + task.id) }
def only_json(): # SAML2 Assertion Consumer Service expects POST request with 'Content-Type': 'application/x-www-form-urlencoded' from IdP if request.method == 'POST' and request.path == '/auth/saml' and request.headers[ 'Content-Type'] == 'application/x-www-form-urlencoded': return if request.path == '/auth/logout': return if request.method in ['POST', 'PUT'] and not request.is_json: raise ApiError( "POST and PUT requests must set 'Content-Type' to 'application/json'", 415)
def serverdensity(): try: incomingAlert = parse_serverdensity(request.json) except ValueError as e: raise ApiError(str(e), 400) if g.get('customer', None): incomingAlert.customer = g.get('customer') add_remote_ip(request, incomingAlert) try: alert = process_alert(incomingAlert) except RejectException as e: raise ApiError(str(e), 403) except Exception as e: raise ApiError(str(e), 500) if alert: return jsonify(status="ok", id=alert.id, alert=alert.serialize), 201 else: raise ApiError("insert or update of ServerDensity alert failed", 500)
def verify_email(hash): user = User.verify_hash(hash, salt='confirm') if user: if user.email_verified: raise ApiError('email already verified', 400) user.set_email_verified() auth_audit_trail.send(current_app._get_current_object(), event='basic-auth-verify-email', message='user confirm email address', user=user.email, customers=[], scopes=[], resource_id=user.id, type='user', request=request) return jsonify(status='ok', message='email address {} confirmed'.format(user.email)) else: raise ApiError('invalid confirmation hash', 400)
def update_user(user_id): if not request.json: raise ApiError('nothing to change', 400) user = User.find_by_id(user_id) if not user: raise ApiError('not found', 404) if request.json.get('email'): user_by_email = User.find_by_email(request.json['email']) if user_by_email and user_by_email.id != user.id: raise ApiError('user with that email already exists', 409) if request.json.get('roles'): want_scopes = Permission.lookup(login='', roles=request.json['roles']) for want_scope in want_scopes: if not Permission.is_in_scope(want_scope, have_scopes=g.scopes): raise ApiError( "Requested scope '{}' not in existing scopes: {}".format( want_scope, ','.join(g.scopes)), 403) updated = user.update(**request.json) admin_audit_trail.send(current_app._get_current_object(), event='user-updated', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=user.id, type='user', request=request) if updated: return jsonify(status='ok', user=updated.serialize) else: raise ApiError('failed to update user', 500)
def create_perm(): try: perm = Permission.parse(request.json) except ValueError as e: raise ApiError(str(e), 400) if perm.match in [ current_app.config['DEFAULT_ADMIN_ROLE'], current_app.config['DEFAULT_USER_ROLE'], current_app.config['DEFAULT_GUEST_ROLE'] ]: raise ApiError('{} role already exists'.format(perm.match), 409) for want_scope in perm.scopes: if not Permission.is_in_scope(want_scope, have_scopes=g.scopes): raise ApiError( "Requested scope '{}' not in existing scopes: {}".format( want_scope, ','.join(g.scopes)), 403) try: perm = perm.create() except Exception as e: raise ApiError(str(e), 500) admin_audit_trail.send(current_app._get_current_object(), event='permission-created', message='', user=g.login, customers=g.customers, scopes=g.scopes, resource_id=perm.id, type='permission', request=request) if perm: return jsonify(status='ok', id=perm.id, permission=perm.serialize), 201 else: raise ApiError('create permission failed', 500)