Skip to content

Commit

Permalink
Add JSON checks
Browse files Browse the repository at this point in the history
Check for all required variables in requests. Refractored endpoints and functions
  • Loading branch information
DResthal committed Mar 17, 2021
1 parent 08355ed commit 95e5d9d
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 103 deletions.
8 changes: 6 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
ACCESS_TOKEN="<github access token>"

AWS_KEY_ID="<aws key>"

JSON_AUTH_TOKEN="<server auth token"
PROD_REPO_URI="<github production repository uri>"
TEST_REPO_URI="<github test repository uri>"

PROD_REPO_URI="DralrinResthal/ProdRepo"
TEST_REPO_URI="Username/Repository"

TEST_FILENAME="test.yml"
PROD_FILENAME="prod.yml"
137 changes: 80 additions & 57 deletions configApi/configApi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from gitUtils import git
from dotenv import load_dotenv
from apilogger import CustomLogger
from datetime import datetime
import config
import json
import logging
Expand Down Expand Up @@ -56,29 +55,26 @@ def is_json_allowed(data: dict) -> tuple:
err_log.warning("No userName")
return ("No userName", 400)

# Check for env
try:
userName = data["env"]
except KeyError as e:
err_log.warning(e)
err_log.warning("No env")
return ("No env", 400)
# Check for env
try:
userName = data["env"]
except KeyError as e:
err_log.warning(e)
err_log.warning("No env")
return ("No env", 400)

return ("JSON response accepted", 200)


def which_env(data: str) -> tuple:
def which_env(env: str) -> tuple:
"""Returns a tuple whose contents are dependent on
which env tag was provided in the request.json.
Returns None if env is invalid.
data: authToken from request.json['authToken']
env: string of env from request.json
Return (git_uri, filename)
"""
data = json.loads(data)
env = data["env"]

if env == "test":
git_uri = os.getenv("TEST_REPO_URI")
filename = os.getenv("TEST_FILENAME")
Expand All @@ -103,6 +99,22 @@ def token_is_valid(authToken: str) -> bool:
return True


def generate_directory_name(data: dict) -> str:
"""Generates the local directory name for the git repo
based on the env and userName supplied.
data: Dict containing either env and userInfo : { userName : }
or the entire request.json
This function should NEVER fail, because it should always be called AFTER
is_json_allowed returns 200, OK and never upon any other result of is_json_allowed.
Therefore, no error checking should be included here as errors should be caught
before this function is ever invoked.
"""
target_dir = f"{data['env']}-repo-{data['userInfo']['userName']}"
return target_dir


@app.route("/getParams", methods=["POST"])
def getParams():
# Log the endpoint access
Expand All @@ -114,7 +126,7 @@ def getParams():
if status != 200:
return (msg, status)

# Validate authToken
# Validate authToken before proceeding
if not token_is_valid(request.json["authToken"]):
return ("Token is not valid. Please provide a valid authToken", 403)

Expand All @@ -125,10 +137,8 @@ def getParams():
if git_uri is None:
return ("Invalid env", 400)

# Generate local directory name
target_dir = (
f"{request.json['env']}-repo-{request.json['userInfo']['userName']}"
)
# Generate directory name
target_dir = generate_directory_name(request.json)

# Ensure that the local directory does not exist
if git.dirname_exists(target_dir):
Expand Down Expand Up @@ -170,52 +180,65 @@ def putParams():
if status != 200:
return msg, status

# Validate supplied authToken
# Validate authToken before proceeding
if not token_is_valid(request.json["authToken"]):
app_log.info(f"Unauthorized request at /putParams. {request.json}")
return "Invalid authToken. This incident has been logged.", 403
else:
now = datetime.now().strftime("%m-%d-%Y %H:%M:%S")
data = request.json
params = data["parameters"]
user = data["userInfo"]
title = f"Config Change - {now}"
msg = f"Created by: {user['userName']} at {now}"
params = file.check_secret(json.dumps(params), delete=True)
# Git Functions Required Order
# 1. Switch to main
# 2. git pull
# 3. git checkout -b <random branch name>
# 4. Update file
# 5. Add and commit new file
# 6. Create PR
# Switching back to main after PR is unecessary now
# These notes have too much future chaos potential to remove.
git.reset_to_main("git_repo")
git.pull("git_repo")
branch = git.new_branch("git_repo")
file.write_file(params, "git_repo/example.yml")
git.add_commit(
"git_repo",
["example.yml"],
"Update to example.yml parameters",
user["userName"],
user["userEmail"],
)
git.create_pr(
"devblueray/TestConfigs",
"git_repo",
os.getenv("ACCESS_TOKEN"),
title,
msg,
branch,
"main",

# Get filename based on env
git_uri, filename = which_env(request.json["env"])

# Generate directory name
target_dir = generate_directory_name(request.json)

# Checking that the directory name generated is present, if not, the request was bad.
if not os.path.exists(target_dir):
err_log.warning(
f"{target_dir} does not exist. Bad JSON request. \n {request.json}"
)
return (
jsonify({"fileUpdate": "Success", "Add and Commit": "Success"}),
200,
"Something went wrong. Please ensure your authToken, userName and env are the same as when you created your clone.",
400,
)

# Encrypt secret parameters
enc_params = file.check_secret(
json.dumps(request.json["parameters"]), delete=True
)

# I think that some of this needs to be moved out of putParams
# I'm thinking that create_pr could be in a cleanup endpoint that also deletes
# the local directory, called AFTER storeParams.
# Required Order of Git Functions
# 1. Switch to main
# 2. git pull
# 3. git checkout -b <random branch name>
# 4. Update file
# 5. Add and commit new file
# 6. Create PR
# Switching back to main after PR is unecessary now
# These notes have too much future chaos potential to remove.
git.reset_to_main(target_dir)
git.pull(target_dir)
new_branch = git.new_branch(target_dir)
file.write_file(enc_params, filename=f"{target_dir}/{filename}")
git.add_commit(
target_dir,
[filename],
"Updated yaml parameters",
request.json["userInfo"]["userName"],
request.json["userInfo"]["userEmail"],
)
git.create_pr(
uri=git_uri,
user=request.json["userInfo"]["userName"],
dir=target_dir,
branch_name=new_branch,
)

app_log.info(f"Created PR for {git_uri}")
return "PR Created", 200


@app.route("/storeParams", methods=["POST"])
def storeParams():
Expand Down
10 changes: 5 additions & 5 deletions configApi/fileUtils/aws.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def encrypt(data: str) -> str:
data = bytes(data.encode("ascii"))
kms = boto3.client("kms", region_name="us-east-1")
res = kms.encrypt(
KeyId=os.getenv("KEY_ID"),
KeyId=os.getenv("AWS_KEY_ID"),
Plaintext=data,
EncryptionAlgorithm="SYMMETRIC_DEFAULT",
)
Expand All @@ -39,7 +39,7 @@ def decrypt(data: str) -> str:
kms = boto3.client("kms", region_name="us-east-1")
res = kms.decrypt(
CiphertextBlob=data,
KeyId=os.getenv("KEY_ID"),
KeyId=os.getenv("AWS_AWS_KEY_ID"),
EncryptionAlgorithm="SYMMETRIC_DEFAULT",
)

Expand All @@ -60,16 +60,16 @@ def store(data: str, prefix: str) -> dict:
for key in data.keys():
if data[key]["secret"]:
res = ssm.put_parameter(
Name=f'/{prefix}/{key}',
Name=f"/{prefix}/{key}",
Description=data[key]["comment"],
Value=data[key]["value"],
Type="SecureString",
KeyId=os.getenv("KEY_ID"),
KeyId=os.getenv("AWS_KEY_ID"),
Overwrite=True,
)
else:
res = ssm.put_parameter(
Name=f'/{prefix}/{key}',
Name=f"/{prefix}/{key}",
Description=data[key]["comment"],
Value=data[key]["value"],
Type="String",
Expand Down
2 changes: 1 addition & 1 deletion configApi/fileUtils/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ def write_file(data: str, filename: str) -> None:
return f"Invalid JSON: {e}"

yaml = ruamel.yaml.YAML()
original = json.loads(read_file(filename))
original = json.loads(read_yaml(filename))
# Updates the original dict, not data
# Does not "return" a value so cannot reassign to data
original.update(data)
Expand Down
48 changes: 26 additions & 22 deletions configApi/gitUtils/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from github import Github
from git import Actor
from shutil import rmtree
from datetime import datetime
import traceback
import git
import sys
Expand Down Expand Up @@ -52,7 +53,7 @@ def clone(uri: str, target: str) -> None:
"""
token = os.getenv("ACCESS_TOKEN")
remote = f"https://{token}:x-oauth-basic@{uri}"
remote = f"https://{token}:x-oauth-basic@github.com/{uri}.git"
try:
git.Repo.clone_from(remote, target)
git_log.info(f"Repository '{uri}' has been cloned to {target}")
Expand Down Expand Up @@ -127,27 +128,30 @@ def pull(repo: str) -> None:
origin.pull()


def create_pr(
lrepo: str,
dir: str,
token: str,
title: str,
body: str,
head: str,
base: str,
) -> None:
"""Create a new pull request
repo: Repository in User/Repository format
dir: local directory name
token: Github access token
title: Title of new pull request
body: Body of pull request
head: Branch to merge (dev -> main, this is dev)
base: Branch to merge new branch into (dev -> main, this is main)
def create_pr(uri: str, dir: str, user: str, branch_name: str) -> None:
"""Create a new pull request, always from <branch> to <main>
uri: Git repo uri, User/Repository
dir: Local directory name
user: Username to include in PR
branch_name: Branch to create PR from
"""
# Generating variables
now = datetime.now().strftime("%m-%d-%Y %H:%M:%S")
title = f"Config Change - {now}"
msg = f"Created by: {user} at {now}"

# Move into local directory
os.chdir(dir)
g = Github(token)
repo = g.get_repo(lrepo)
repo.create_pull(title=title, body=body, head=head, base=base)

# Instanciate Github using current local directory
g = Github(os.getenv("ACCESS_TOKEN"))

# Instanciate repository in current local directory
repo = g.get_repo(uri)

# Create the PR
repo.create_pull(title=title, body=msg, head=branch_name, base="main")

# Moved back out of current local directory
os.chdir("../")
32 changes: 16 additions & 16 deletions requests.rest
Original file line number Diff line number Diff line change
Expand Up @@ -71,24 +71,24 @@ POST http://127.0.0.1:8000/putParams
content-type: application/json

{
"authToken": "FXbWZBmGstqCJP8D7P3kzguthu479dFv",
"userInfo": {
"userName": "Bob Pickles",
"userEmail": "[email protected]"
"authToken": "FXbWZBmGstqCJP8D7P3kzguthu479dFv",
"userInfo": {
"userName": "Bob Pickles",
"userEmail": "[email protected]"
},
"env": "test",
"parameters": {
"a": {
"value": "some dumb value",
"secret": true,
"comment": "some dumb comment"
},
"env": "prod",
"parameters": {
"a": {
"value": "some dumb value",
"secret": true,
"comment": "some dumb comment"
},
"b": {
"value": "another dumb value",
"secret": false,
"comment": "another dumb comment"
}
"b": {
"value": "another dumb value",
"secret": false,
"comment": "another dumb comment"
}
}
}

###
Expand Down

0 comments on commit 95e5d9d

Please sign in to comment.