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

submit50 v2 #15

Merged
merged 31 commits into from
Jan 17, 2017
Merged

submit50 v2 #15

merged 31 commits into from
Jan 17, 2017

Conversation

brianyu28
Copy link
Member

@brianyu28 brianyu28 commented Jan 6, 2017

Ported submit50 to submit50.py. Notable differences between the two:

  • Two-factor authentication. Here I had to rely less on github3.py and more on GitHub's API directly, because github3.py requires that every API query trigger another authentication code request (and it didn't make sense to have students input two or three different codes just to submit a problem set). Instead, this uses GitHub access tokens so that users need only enter an authentication code once.
  • Academic honesty / regret clause reminder after submission. Wording is directly from the syllabus.
  • --checkout now supports leaving [problem] blank as well as specifying a list of usernames as command line arguments. Piping a text file with usernames one per line works too. So possible uses now are:
    • submit50 --checkout [problem]
    • submit50 --checkout [problem] @alice @bob
    • cat usernames.txt | submit50 --checkout [problem]
  • Timestamps on commits are in EST instead of GMT.

After installing the requirements in requirements.txt, for this to work in the IDE you'll also need to execute the following to fix permissions issues:

sudo chmod a+r /usr/local/lib/python3.4/dist-packages/getch.cpython-34m.so
sudo chmod a+r /usr/local/lib/python3.4/dist-packages/termcolor.py
sudo chmod -R a+rX /usr/local/lib/python3.4/dist-packages/pexpect/
sudo chmod -R a+rX /usr/local/lib/python3.4/dist-packages/github3/
sudo chmod -R a+rX /usr/local/lib/python3.4/dist-packages/pytz/

@@ -40,7 +53,7 @@ def main():

# submit50 --checkout
elif sys.argv[1] == "--checkout":
checkout(sys.argv[1:])
checkout(sys.argv[2:])
Copy link
Member

Choose a reason for hiding this comment

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

Shall we add support for -c too, for consistency with -h?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added!

@@ -74,7 +85,7 @@ def authenticate():
print()
break
elif ch == "\177": # DEL
if len(password) > 1:
if len(password) > 0:
Copy link
Member

Choose a reason for hiding this comment

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

Good catch, ty.

_, EXCLUDE = tempfile.mkstemp()
url = "https://raw.githubusercontent.com/submit50/submit50/{}/exclude?{}".format(problem, time.time())
url = "https://raw.githubusercontent.com/{0}/{0}/{1}/exclude".format(ORG_NAME, problem)
Copy link
Member

Choose a reason for hiding this comment

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

Mind Googling to see if these raw.githubusercontent.com URLs have existed for a while / are likely to? Guessing so, but would be good to know if GitHub has some fine print like "these might change at any time."

Copy link
Member Author

Choose a reason for hiding this comment

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

Looks like raw.githubusercontent.com started being used in April 2014 and has been used by GitHub consistently since then. When they made the 2014 change, old links redirected to the new correct ones, so these links should likely continue to work.

run("git config user.email {}".format(shlex.quote(email)))
run("git config user.name {}".format(shlex.quote(username)))

run("git symbolic-ref HEAD {}".format(shlex.quote("refs/heads/{}".format(problem))))
Copy link
Member

Choose a reason for hiding this comment

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

Recall if this double formatting was necessary? I forget offhand what corner case I was worried about. Something with quotes maybe?

Copy link
Member Author

Choose a reason for hiding this comment

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

Hm it doesn't seem necessary. Given that the refs/heads/ part will always be the same, I adjusted this to only require one .format().

shlex.quote("https://{}@github.com/submit50/{}".format(username, username)),
shlex.quote(GIT_DIR)
))
shlex.quote("https://{}@github.com/{}/{}".format(username, ORG_NAME, username)),
Copy link
Member

Choose a reason for hiding this comment

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

Some of these lines should be indented?

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed!

teardown()
print(termcolor.colored("Submitted {}! See https://github.com/{}/{}/tree/{}.".format(
problem, ORG_NAME, username, problem), "green"))
print("Academic Honesty reminder: If you commit some act that is not reasonable but bring it to the attention of the course’s heads within 72 hours, the course may impose local sanctions that may include an unsatisfactory or failing grade for work submitted, but the course will not refer the matter for further disciplinary action except in cases of repeated acts.")
Copy link
Member

Choose a reason for hiding this comment

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

Instead of this, let's put a new prompt above the first push that asks "Keeping in mind the course's policy on academic honesty, are you sure you want to submit these files?"

Copy link
Member Author

Choose a reason for hiding this comment

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

Done!

"""Get one-time authentication code."""
# send authentication request
requests.post("https://api.github.com/authorizations", auth=two_factor.auth, data={"scopes":["repo", "user"]})
Copy link
Member

Choose a reason for hiding this comment

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

Will two_factor.auth definitely not be None at this point?

Copy link
Member

Choose a reason for hiding this comment

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

Do we definitely need to provide scopes to trigger an authorization code? Is this what the github3 library does too to trigger it?

Copy link
Member Author

Choose a reason for hiding this comment

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

two_factor.auth will definitely not be None. The two_factor() function is only ever called after two_factor.auth is set to (username, password).

Ah, you're right, scopes is necessary later to get the token but not strictly necessary here. Removed!

res = requests.post("https://api.github.com/authorizations", auth=two_factor.auth,
data=data,
headers={"X-GitHub-OTP": str(code)})
if "token" in res.json():
Copy link
Member

Choose a reason for hiding this comment

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

If response is non-200, might res.json() return something other than a list?

Copy link
Member Author

Choose a reason for hiding this comment

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

Definitely possible. Added a check to make sure the status code is 201 Created, per GitHub's authorizations API.

while True:
print("Authentication Code: ", end="", flush=True)
code = input()
if code:
break
data = json.dumps({"scopes":["repo", "user"], "note":"{} {}".format(ORG_NAME, timestamp)})
Copy link
Member

Choose a reason for hiding this comment

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

What's the note for?

Copy link
Member Author

Choose a reason for hiding this comment

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

GitHub requires a token description (passed in as note) for a user-identifiable name for the token. Users can see those token descriptions in their settings when they log into GitHub's website.

username = user.login
email = "{}@users.noreply.github.com".format(user.login)
two_factor.auth = (username, password)
github = github3.login(username, password, two_factor_callback=two_factor)
Copy link
Member

Choose a reason for hiding this comment

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

Do we need this anymore?

@brianyu28
Copy link
Member Author

Comments should be addressed!

@brianyu28 brianyu28 changed the base branch from 2fa to master January 9, 2017 19:19
@dmalan dmalan merged commit 627692a into cs50:master Jan 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants