Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3"
JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6"
MbedTLS = "739be429-bea8-5141-9913-cc70e7f3736d"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
SHA = "ea8e919c-243c-51af-8825-aaa63cd721ce"
Sockets = "6462fe0b-24de-5631-8697-dd941f90decc"
SodiumSeal = "2133526b-2bfb-4018-ac12-889fb3908a75"
URIs = "5c2747f8-b7ea-4ff2-ba2e-563bfd36b1d4"
Expand All @@ -17,6 +19,8 @@ HTTP = "1.10.17" # Must be >= 1.10.17, see https://github.com/JuliaWeb/GitHub.jl
URIs = "1.6" # Must be >= 1.6, see https://github.com/JuliaWeb/GitHub.jl/pull/225
JSON = "0.19, 0.20, 0.21"
MbedTLS = "0.6, 0.7, 1"
Random = "1"
SHA = "0.7.0, 1"
SodiumSeal = "0.1"
julia = "1.6"

Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,13 @@ You can inspect which fields are available for a type `G<:GitHubType` by calling

GitHub.jl implements a bunch of methods that make REST requests to GitHub's API. The below sections list these methods (note that a return type of `Tuple{Vector{T}, Dict}` means the result is [paginated](#pagination)).

These methods all accept keyword arguments which control how the request is made to github:

- `max_retries::Int=5`: how many retries to attempt in requesting the resources. Retries are only made for idempotent requests ("GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE") and delays respect GitHub [rate limit headers](https://docs.github.com/en/rest/using-the-rest-api/rate-limits-for-the-rest-api?apiVersion=2022-11-28#checking-the-status-of-your-rate-limit).
- `verbose::Bool=true`: whether or not to log retries as Info level logs
- `max_sleep_seconds::Real=60*20`: if GitHub.jl intends to sleep for longer than `max_sleep_seconds` before retrying, e.g. due to rate limit headers from GitHub, throws an `RetryDelayException` instead.
- `respect_mutation_delay::Bool=true`: whether or not to ensure at least 1s has passed since the last mutation has been submitted to the GitHub API (tracked separately per auth object), following GitHub's [recommended practice](https://docs.github.com/en/rest/using-the-rest-api/best-practices-for-using-the-rest-api?apiVersion=2022-11-28#pause-between-mutative-requests).

#### Users and Organizations

| method | return type | documentation |
Expand Down
4 changes: 3 additions & 1 deletion src/GitHub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ module GitHub

using Dates
using Base64
using Random
using SHA: sha256

##########
# import #
Expand Down Expand Up @@ -44,7 +46,7 @@ export # auth.jl
authenticate

export # requests.jl
rate_limit
rate_limit, RetryDelayException

##################################
# Owners (organizations + users) #
Expand Down
25 changes: 25 additions & 0 deletions src/utils/auth.jl
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,28 @@ function Base.show(io::IO, a::OAuth2)
token_str = a.token[1:6] * repeat("*", length(a.token) - 6)
print(io, "GitHub.OAuth2($token_str)")
end

###########
# Hashing #
###########

# These are used to implement the 1s mutation delay on a per-auth basis
# See also `wait_for_mutation_delay`. We want it to be difficult to
# invert the hash to recover the token, as the hash is stored in a global dict
# in this module.
let
# this would be better as OncePerProcess to get fresh salts per session, but we support old Julia's so we'll
# have one per precompilation.
salt = randstring(RandomDevice(), 20)
global uint64_hash(str::AbstractString) = first(reinterpret(UInt64, sha256(string(salt, str))[1:8]))
end

get_auth_hash(auth::OAuth2) = uint64_hash(auth.token)

get_auth_hash(auth::UsernamePassAuth) = uint64_hash(string(auth.username, "##", auth.password))

get_auth_hash(::AnonymousAuth) = UInt64(0)

# note this will give different hashes for different JWTs from the same key,
# meaning in that case the 1s mutation delay will apply per-JWT instead of per-key
get_auth_hash(auth::JWTAuth) = uint64_hash(auth.JWT)
Loading
Loading