Как вы используете механизмы аутентификации Snap во время одного запроса POST?

#authentication #haskell #haskell-snap-framework

#аутентификация #haskell #haskell-snap-framework

Вопрос:

Я работаю над веб-приложением на основе Haskell Snap, и я хочу предоставить конечную точку API, которая будет вызываться удаленной службой без установления сеанса аутентификации априори; однако я хочу, чтобы этот запрос был аутентифицирован, поэтому учетные данные должны быть предоставлены во время запроса.

Вы могли бы представить запрос, содержащий четыре поля:

  • Имя пользователя
  • Пароль
  • идентификатор полезной нагрузки
  • файл полезной нагрузки

Идентификатор полезной нагрузки и файл могут не иметь отношения к этому вопросу, но я включаю их, потому что мне (а) нужно поддерживать загрузку файлов в этом запросе (который, как я понимаю, ограничивает кодировку, используемую для отправки полей) и (б) необходимо получить хотя бы одно поле, не относящееся к файлу. Комбинация этих вещей вызвала некоторые трудности, когда я настроил это без аутентификации, поэтому, возможно, это актуально.

На языке Snap давайте вызовем этот обработчик uploadHandler .

Как указано выше, у меня это работает нормально без аутентификации, с настройкой, подобной этой:

 uploadHandler :: Handler App App ()
uploadHandler = do
  -- collect files / form fields and process as needed.

-- and using the routes:
routes :: [(ByteString, Handler App App ())]
routes = [ ("/login",    with auth handleLoginSubmit)
         , ("/logout",   with auth handleLogout)
         , ("/new_user", with auth handleNewUser)

         -- handle the upload:
         , ("/upload",   handleUpload)
         ]
 

Наивное решение — просто добавить «с авторизацией» и изменить тип handleUpload :

 uploadHandler :: Handler App (AuthManager App) ()
uploadHandler = do
  -- collect files / form fields and process as needed.

-- and using the routes:
routes :: [(ByteString, Handler App App ())]
routes = [ ("/login",    with auth handleLoginSubmit)
         , ("/logout",   with auth handleLogout)
         , ("/new_user", with auth handleNewUser)

         -- handle the upload, with auth:
         , ("/upload",   with auth handleUpload)
         ]
 

Однако для этого, по-видимому, требуется два запроса: (i) аутентифицировать и установить сеанс, (ii) отправить POST-запрос, содержащий фактическую полезную нагрузку.

Я нашел способ сделать это в одном запросе, но, похоже, должны быть более элегантные средства. Вот пример ограниченного обработчика POST, который я взломал вместе:

 restrictedPOST :: Handler App (AuthManager App) ()
restrictedPOST = do
  mName <- getPostParam "username"
  mPass <- getPostParam "password"
  let uName = C8.unpack $ fromMaybe "" mName
      pass = ClearText $ fromMaybe "" mPass

  authResult <- loginByUsername (T.pack uName) pass False
  case authResult of
    Left authFail -> writeText "Could not log in"
    Right user    -> writeText (T.append "Hello " (userLogin user))
 

Есть ли что-то вроде ‘with auth’, что я могу использовать вместо превращения этого примера ( restrictedPOST ) в комбинатор? Я понимаю, что может потребоваться знать, из каких полей получать учетные данные, но я также очень мало знаю о веб-сервисах (может быть, есть другие способы? Возможно, это не проблема, и я просто не знаю, как проверить аутентификацию для запросов POST. Я открыт для любых предложений!)

Ответ №1:

Я не думаю, что вы понимаете, что with auth делаете. Это не имеет никакого отношения к тому, требуется ли аутентификация. Все, что он делает, это преобразует a Handler b (AuthManager b) в a Handler b v . Проверка разрешений не выполняется. Ваша функция restrictedPOST имеет правильную идею.

Комментарии:

1. Я думаю, вы были правы… Теперь я лучше понимаю, что делает with auth. Спасибо!