Аутентификация токена JWT с использованием запросов python для DockerHub

#python-3.x #python-requests #dockerhub

#python-3.x #python-запросы #dockerhub

Вопрос:

Я узнал, что редактирование full_description репозитория DockerHub может быть выполнено с помощью JavaScript API, и подумал, что это было бы забавным поводом изучить requests пакет для python. JavaScript API определенно работает, например, при использовании этого простого образа docker.

JS API в основном выполняет

  1. Отправьте POST запрос на https://hub.docker.com/v2/users/login с именем пользователя и паролем. Сервер отвечает token .
  2. Отправьте 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 in requests , такой же, как если бы I GET to auth.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 оставался неизменным все это время, поэтому для любых веб-новичков, таких как я, не делитесь ими 😉