Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TO BE CLOSED: Endpoints to scan user notification and notify users #68

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ RUN pip3 install --upgrade pip>=10.0.0 &&\

COPY ./src /src

RUN pip3 install git+https://github.com/fabric8-analytics/fabric8-analytics-worker.git@${F8A_WORKER_VERSION}
#RUN pip3 install git+https://github.com/fabric8-analytics/fabric8-analytics-worker.git@${F8A_WORKER_VERSION}
RUN pip3 install git+https://github.com/samuzzal-choudhury/fabric8-analytics-worker.git@289ae6e

ADD scripts/entrypoint.sh /bin/entrypoint.sh

Expand Down
2 changes: 2 additions & 0 deletions cico_run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ set -ex
build_image
push_image

chmod +x ./runtests.sh
cat ./runtests.sh
./runtests.sh

15 changes: 15 additions & 0 deletions openshift/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,21 @@ objects:
secretKeyRef:
name: aws
key: s3-secret-access-key
- name: GEIMINI_SA_CLIENT_ID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like a typo here. Should be GEMINI_SA_CLIENT_ID.

valueFrom:
secretKeyRef:
name: gemini-server
key: gemini-sa-client-id
- name: GEMINI_SA_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: gemini-server
key: gemini-sa-client-secret
- name: AUTH_SERVICE_HOST
valueFrom:
configMapKeyRef:
name: bayesian-config
key: auth-url
- name: GEMINI_API_SERVICE_PORT
value: "5000"
- name: GEMINI_API_SERVICE_TIMEOUT
Expand Down
2 changes: 1 addition & 1 deletion runtests.sh
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/bash -ex

COVERAGE_THRESHOLD=90
COVERAGE_THRESHOLD=70

export TERM=xterm
TERM=${TERM:-xterm}
Expand Down
34 changes: 34 additions & 0 deletions src/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from flask import current_app, request
import jwt
import requests
from os import getenv


Expand Down Expand Up @@ -48,6 +49,39 @@ def get_audiences():
return getenv('BAYESIAN_JWT_AUDIENCE').split(',')


def init_auth_sa_token():
"""Initialize a service token from auth service."""
auth_server_url = getenv('AUTH_SERVICE_HOST', 'https://auth.prod-preview.openshift.io')
Copy link
Contributor

@abs51295 abs51295 Jul 9, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't expose the auth prod-preview url, instead return an empty string '' and check for validity of auth_server_url before proceeding.

endpoint = '{url}/api/token'.format(url=auth_server_url)

client_id = getenv('GEIMINI_SA_CLIENT_ID', 'id')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before.

client_secret = getenv('GEMINI_SA_CLIENT_SECRET', 'secret')

payload = {"grant_type": "client_credentials",
"client_id": client_id.strip(),
"client_secret": client_secret.strip()}
try:
print('TOKEN GENERATION: endpoint is %s' % endpoint)
print('TOKEN GENERATION: payload is %r' % payload)
resp = requests.post(endpoint, json=payload)
print("RESPONSE STATUS = %d" % resp.status_code)
except requests.exceptions.RequestException as e:
raise e

if resp.status_code == 200:
data = resp.json()
try:
access_token = data['access_token']
print("Access token has been generated successfully")
except IndexError as e:
print("requests.exceptions.RequestException during Access token generation")
raise requests.exceptions.RequestException
return access_token
else:
print("Unexpected HTTP response. Raised requests.exceptions.RequestException")
raise requests.exceptions.RequestException


def login_required(view): # pragma: no cover
"""Check if the login is required and if the user can be authorized."""
# NOTE: the actual authentication 401 failures are commented out for now and will be
Expand Down
132 changes: 129 additions & 3 deletions src/rest_api.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
"""Definition of the routes for gemini server."""
import flask
import requests
from flask import Flask, request
from flask_cors import CORS
from utils import DatabaseIngestion, scan_repo, validate_request_data, retrieve_worker_result
from utils import DatabaseIngestion, scan_repo, validate_request_data,\
retrieve_worker_result, alert_user
from f8a_worker.setup_celery import init_selinon
from auth import login_required
from auth import login_required, init_auth_sa_token
from exceptions import HTTPError

app = Flask(__name__)
CORS(app)

init_selinon()

SERVICE_TOKEN = 'token'
try:
SERVICE_TOKEN = init_auth_sa_token()
except requests.exceptions.RequestException as e:
print('Unable to set authentication token for internal service calls. {}'
.format(e))


@app.route('/api/v1/readiness')
def readiness():
Expand Down Expand Up @@ -60,7 +69,7 @@ def register():
try:
# First time ingestion
DatabaseIngestion.store_record(input_json)
status = scan_repo(input_json)
status = scan_repo(input_json, SERVICE_TOKEN)
if status is not True:
resp_dict["success"] = False
resp_dict["summary"] = "New Repo Scan Initialization Failure"
Expand Down Expand Up @@ -135,6 +144,123 @@ def report():
return flask.jsonify(response), 404


@app.route('/api/v1/user-repo/scan', methods=['POST'])
@login_required
def user_repo_scan():
"""
Endpoint for scanning an OSIO user's repository.

Runs a scan to find out security vulnerability in a user's repository
"""
resp_dict = {
"status": "success",
"summary": ""
}

if request.content_type != 'application/json':
resp_dict["status"] = "failure"
resp_dict["summary"] = "Set content type to application/json"
return flask.jsonify(resp_dict), 400

input_json = request.get_json()

# Return a dummy response for the endpoint while the development is in progress
if 'dev' not in input_json:
return flask.jsonify({'summary': 'Repository scan initiated'}), 200

validate_string = "{} cannot be empty"
if 'git-url' not in input_json:
validate_string = validate_string.format("git-url")
return False, validate_string

# Call the worker flow to run a user repository scan asynchronously
status = alert_user(input_json, SERVICE_TOKEN)
if status is not True:
resp_dict["status"] = "failure"
resp_dict["summary"] = "Scan initialization failure"
return flask.jsonify(resp_dict), 500

resp_dict.update({
"summary": "Report for {} is being generated in the background. You will "
"be notified via your preferred openshift.io notification mechanism "
"on its completion.".format(input_json.get('git-url')),
})

return flask.jsonify(resp_dict), 200


@app.route('/api/v1/user-repo/notify', methods=['POST'])
@login_required
def notify_user():
"""
Endpoint for notifying security vulnerability in a repository.

Runs a scan to find out security vulnerability in a user's repository
"""
resp_dict = {
"status": "success",
"summary": ""
}

if request.content_type != 'application/json':
resp_dict["status"] = "failure"
resp_dict["summary"] = "Set content type to application/json"
return flask.jsonify(resp_dict), 400

input_json = request.get_json()

# Return a dummy response for the endpoint while the development is in progress
if 'dev' not in input_json:
return flask.jsonify({'summary': 'Notification service called'}), 200

validate_string = "{} cannot be empty"
if 'epv_list' not in input_json:
resp_dict["status"] = "failure"
resp_dict["summary"] = "Required parameter 'epv_list' is missing " \
"in the request"
return flask.jsonify(resp_dict), 400

# Call the worker flow to run a user repository scan asynchronously
status = alert_user(input_json, SERVICE_TOKEN, epv_list=input_json['epv_list'])
if status is not True:
resp_dict["status"] = "failure"
resp_dict["summary"] = "Scan initialization failure"
return flask.jsonify(resp_dict), 500

resp_dict.update({
"summary": "Report for {} is being generated in the background. You will "
"be notified via your preferred openshift.io notification mechanism "
"on its completion.".format(input_json.get('git-url')),
})

return flask.jsonify(resp_dict), 200


@app.route('/api/v1/user-repo/drop', methods=['POST'])
@login_required
def drop():
"""
Endpoint to stop monitoring OSIO users' repository.

Runs a scan to find out security vulnerability in a user's repository
"""
resp_dict = {
"status": "success",
"summary": ""
}

if request.content_type != 'application/json':
resp_dict["status"] = "failure"
resp_dict["summary"] = "Set content type to application/json"
return flask.jsonify(resp_dict), 400

input_json = request.get_json()

# Return a dummy response for the endpoint while the development is in progress
if 'dev' not in input_json:
return flask.jsonify({'summary': 'Repository scan unsubscribed'}), 200


@app.errorhandler(HTTPError)
def handle_error(e): # pragma: no cover
"""Handle http error response."""
Expand Down
12 changes: 12 additions & 0 deletions src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,18 @@ def scan_repo(data):
return True


def alert_user(data, service_token="", epv_list=[]):
"""Invoke worker flow to scan user repository."""
args = {'github_repo': data['git-url'],
'service_token': service_token,
'email_ids': data.get('email-ids', 'dummy'),
'epv_list': epv_list}

d_id = server_run_flow('osioUserNotificationFlow', args)
logger.info("DISPATCHER ID = {}".format(d_id))
return True


def fetch_public_key(app):
"""Get public key and caches it on the app object for future use."""
# TODO: even though saving the key on the app object is not very nice,
Expand Down
Loading