#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. Спасибо!