#python-3.x #python-requests #dockerhub
#python-3.x #python-запросы #dockerhub
Вопрос:
Я узнал, что редактирование full_description
репозитория DockerHub может быть выполнено с помощью JavaScript API, и подумал, что это было бы забавным поводом изучить requests
пакет для python. JavaScript API определенно работает, например, при использовании этого простого образа docker.
JS API в основном выполняет
- Отправьте
POST
запрос наhttps://hub.docker.com/v2/users/login
с именем пользователя и паролем. Сервер отвечаетtoken
. - Отправьте
PATCH
запрос конкретномуhttps://hub.docker.com/v2/repositories/{user or org}/{repo}
, убедившись, что заголовок имеетAuthorization: JWT {token}
, и в данном случае с телом содержимого{"full_description":"...value..."}
.
Что беспокоит, так это то, что PATCH
запрос на стороне python получает ответ 200 от сервера (если вы намеренно установили неверный токен аутентификации, вам будет отказано, как и ожидалось). Но этот ответ на самом деле содержит текущую информацию (не исправленную информацию).
Единственные «открытия», которые я сделал:
-
Если вы добавите материал для ведения журнала отладки, там будет 301. Но это тот же URL для стороны javascript, так что это не имеет значения?
send: b'{"full_description": "TEST"}' reply: 'HTTP/1.1 301 MOVED PERMANENTLYrn'
-
Токен, полученный при выполненииЭто неверно. Они разные, я не знаю, как я думал, что они одинаковые.POST
inrequests
, такой же, как если бы IGET
toauth.docker.io
описал в разделеПолучение токена на предъявителя» здесь. Примечательно, что я не указывал пароль (просто сделалcurl -X GET ...
).
Этот второй заставляет меня чувствовать, что я пропускаю шаг. Как будто мне нужно декодировать токен или что-то в этомроде? Я не знаю, что еще с этим делать, особенно 200
ответ от PATCH
, несмотря на отсутствие изменений.
Код:
import json
from textwrap import indent
import requests
if __name__ == "__main__":
username = "<< SET THIS VALUE >>"
password = "<< SET THIS VALUE >>"
repo = "<< SET THIS VALUE >>"
base_url = "https://hub.docker.com/v2"
login_url = f"{base_url}/users/login"
repo_url = f"{base_url}/repositories/{username}/{repo}"
# NOTE: if I use a `with requests.Session()`, then I'll get
# CSRF Failed: CSRF token missing or incorrect
# Because I think that csrftoken is only valid for login page (?)
# Get login token and create authorization header
print("==> Logging into DockerHub")
tok_req = requests.post(login_url, json={"username": username, "password": password})
token = tok_req.json()["token"]
headers = {"Authorization": f"JWT {token}"}
print(f"==> Sending PATCH request to {repo_url}")
payload = {"full_description": "TEST"}
patch_req = requests.patch(repo_url, headers=headers, json=payload)
print(f" Response (status code: {patch_req.status_code}):")
print(indent(json.dumps(patch_req.json(), indent=2), " "))
Комментарии:
1. Можете ли вы проверить тип
token
? Я не уверен, правильно ли я помню, но в python иногда что-то преобразуется в bytestring, и внезапно это больше не работает.2. Хм, хорошая мысль, я об этом не подумал.
type(token)
Естьstr
, но я остановлюсь на этом немного подробнее. Возможно, есть какие-то ручки, которые я могу включить, какrequests
или, возможноurllib3
, фактически декодирует переданный ответ.3. Можете ли вы также подтвердить, что полученный вами токен является реальным JWT?
Getting a bearer token
Размещенная вами ссылка, похоже, говорит о том, что вам нужно закодировать ответ, чтобы получить функциональный JWT, но я не уверен, не зная, каков ответ на запрос.4. Отредактировано, я понятия не имею, почему я думал, что они одинаковы… это не так.
Can you also confirm that the token you get is an actual JWT?
Я понятия не имею, но я боюсь публиковать ответ, который приходит после указания моего имени пользователя / пароля 😉 Я играю сjwt
модулем , я думаю, мне нужно либо закодировать, либо декодировать что-то, как вы говорите. Я хотел бы помочь вам, помогите мне, но я не знаю, чего я не знаю x0 Я ценю, что вы предлагаете предложения, хотя я буду продолжать работать 🙂5. Пожалуйста, не могли бы вы прояснить проблему. Я не понимаю, что вы считаете неожиданным в том, как ведет себя запрос / ответ на ИСПРАВЛЕНИЕ. Вы упомянули «200 ответов от ИСПРАВЛЕНИЯ, несмотря на отсутствие изменений», но для меня это ожидаемо. Если бы я отправлял идентичный запрос на исправление несколько раз, я ожидал бы получать ответ
200 OK
каждый раз.
Ответ №1:
Дополнительная информация, связанная с вашей проблемой CSRF при использовании запросов.Сессия ():
Похоже, что Docker Hub не распознает csrftoken
именованный заголовок / cookie (имя по умолчанию для следующего cookie) при выполнении запросов в этом случае. Вместо этого, при использовании заголовка X-CSRFToken
в следующих запросах, CSRF идентифицируется как допустимый. Возможно, причина в шаблоне токена cookie-to-header.
После обновления заголовка сеанса файлом cookie ответа на вход
s.headers.update({"X-CSRFToken": s.cookies.get("csrftoken")})
Больше нет необходимости устанавливать токен JWT вручную для дальнейших запросов — токен уже работает как cookie.
Извините, недостаточно привилегий, чтобы просто комментировать, но я думаю, что это достаточно актуально.
Комментарии:
1. потрясающе, спасибо, что поделились (и добро пожаловать в SO)! 🙂
2. Спасибо, что позволили мне сделать этот комментарий!
Ответ №2:
Как оказалось, JWT {token}
аутентификация была действительной все время. По-видимому, вам нужен /
в конце URL. Без этого ничего не происходит. Смехотворно!
# ----------------------------------------------------V
repo_url = f"{base_url}/repositories/{username}/{repo}/"
Как и ожидалось, PATCH
затем отвечает обновленным описанием, а не старым описанием. ОГО-ГО!
Важное примечание: это работает у меня с 15 января 2020 года, но в своих поисках я столкнулся с этой проблемой dockerhub, которая, похоже, указывает на то, что если у вас в учетной записи включен 2FA, вы больше не можете редактировать запрос description
с помощью PATCH
. У меня нет 2FA в моей учетной записи, поэтому я могу (по-видимому). Неясно, каким будет будущее этого.
Примечание по теме: токен JWT оставался неизменным все это время, поэтому для любых веб-новичков, таких как я, не делитесь ими 😉