From 235138882650f54410154243cc6c61122e556d6d Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Sun, 3 Dec 2023 03:49:20 +0000 Subject: [PATCH 1/4] feat(auth): WIP support for OAuth2 --- package-lock.json | 302 +++++++++++++++++++ package.json | 1 + package.yaml | 1 + src/Application.hs | 31 +- src/Auth/OAuth2.hs | 58 ++++ src/Foundation/Instances.hs | 6 +- src/Foundation/Type.hs | 1 + src/Foundation/Yesod/Auth.hs | 80 ++++- stack.yaml | 3 + stack.yaml.lock | 551 ++++++++++++++++++----------------- 10 files changed, 757 insertions(+), 277 deletions(-) create mode 100644 src/Auth/OAuth2.hs diff --git a/package-lock.json b/package-lock.json index 8aae86886..5fedced5f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2404,6 +2404,12 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, "array-ify": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", @@ -3519,6 +3525,23 @@ "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", "dev": true }, + "basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "dev": true, + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, "bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -4142,6 +4165,15 @@ } } }, + "content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dev": true, + "requires": { + "safe-buffer": "5.2.1" + } + }, "content-type": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", @@ -4517,6 +4549,12 @@ "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", "dev": true }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "dev": true + }, "copy-webpack-plugin": { "version": "11.0.0", "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", @@ -5036,6 +5074,12 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, "destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -5629,6 +5673,12 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -5666,6 +5716,112 @@ } } }, + "express": { + "version": "4.18.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", + "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.1", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "body-parser": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", + "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + } + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -5881,12 +6037,24 @@ "mime-types": "^2.1.12" } }, + "forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", "dev": true }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, "fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -6441,6 +6609,15 @@ "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", "dev": true }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", @@ -6541,6 +6718,12 @@ "loose-envify": "^1.0.0" } }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -6825,6 +7008,12 @@ } } }, + "jose": { + "version": "4.15.4", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.15.4.tgz", + "integrity": "sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==", + "dev": true + }, "js-cookie": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.1.tgz", @@ -7822,6 +8011,12 @@ } } }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7846,6 +8041,12 @@ "underscore": "*" } }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", @@ -9854,6 +10055,27 @@ } } }, + "oauth2-mock-server": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/oauth2-mock-server/-/oauth2-mock-server-7.1.1.tgz", + "integrity": "sha512-4/PdPZLySsC68IoiO79BKpr5Rv2j2+WgFZskox7bzSlsXqoX8Nm9OWm3IXB0HQ7xJCbzcR4vvvcDe6UnA/UIiw==", + "dev": true, + "requires": { + "basic-auth": "^2.0.1", + "cors": "^2.8.5", + "express": "^4.18.2", + "is-plain-object": "^5.0.0", + "jose": "^4.15.4" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + } + } + }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -10064,6 +10286,12 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "dev": true + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -10818,6 +11046,16 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, + "proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "requires": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + } + }, "psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -11557,6 +11795,58 @@ } } }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "requires": { + "ms": "2.0.0" + }, + "dependencies": { + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + } + } + }, "serialize-javascript": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", @@ -11566,6 +11856,18 @@ "randombytes": "^2.1.0" } }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, "setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", diff --git a/package.json b/package.json index e7e8a6e47..f31e8c86b 100644 --- a/package.json +++ b/package.json @@ -86,6 +86,7 @@ "mini-css-extract-plugin": "^2.6.0", "npm-run-all": "^4.1.5", "null-loader": "^4.0.1", + "oauth2-mock-server": "^7.1.1", "optimize-css-assets-webpack-plugin": "^6.0.1", "postcss-loader": "^7.0.0", "postcss-preset-env": "^7.7.1", diff --git a/package.yaml b/package.yaml index c6e1a8bcb..c976fcbcb 100644 --- a/package.yaml +++ b/package.yaml @@ -6,6 +6,7 @@ dependencies: - yesod-core - yesod-persistent - yesod-auth + - yesod-auth-oauth2 - yesod-static - yesod-form - yesod-persistent diff --git a/src/Application.hs b/src/Application.hs index 45f24768e..c6f2cb68c 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost +-- SPDX-FileCopyrightText: 2023 Gregor Kleen ,Sarah Vaupel ,Sarah Vaupel ,Steffen Jost ,David Mosbach -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -60,7 +60,9 @@ import System.Directory import Jobs import qualified Data.Text.Encoding as Text +import qualified Data.Text as Text +import Yesod.Auth.OAuth2.AzureADv2 (oauth2AzureADv2) import Yesod.Auth.Util.PasswordStore import qualified Data.ByteString.Lazy as LBS @@ -124,6 +126,8 @@ import Handler.Utils.Memcached (manageMemcachedLocalInvalidations) import qualified System.Clock as Clock +import Data.Maybe (fromJust) + import Utils.Avs -- Import all relevant handler modules here. @@ -166,6 +170,8 @@ import Servant.API import Servant.Client import Network.HTTP.Client.TLS (mkManagerSettings) +import Auth.OAuth2 + -- This line actually creates our YesodDispatch instance. It is the second half -- of the call to mkYesodData which occurs in Foundation.hs. Please see the @@ -235,7 +241,7 @@ makeFoundation appSettings''@AppSettings{..} = do -- from there, and then create the real foundation. let mkFoundation :: _ -> (forall m. MonadIO m => Custom.Pool' m DBConnLabel DBConnUseState SqlBackend) -> _ - mkFoundation appSettings' appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appMemcachedLocal appUploadCache appVerpSecret appAuthKey appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery = UniWorX{..} + mkFoundation appSettings' appConnPool appSmtpPool appLdapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appMemcachedLocal appUploadCache appVerpSecret appAuthKey appAuthPlugins appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery = UniWorX{..} tempFoundation = mkFoundation (error "appSettings' forced in tempFoundation") (error "connPool forced in tempFoundation") @@ -252,10 +258,12 @@ makeFoundation appSettings''@AppSettings{..} = do (error "MinioConn forced in tempFoundation") (error "VerpSecret forced in tempFoundation") (error "AuthKey forced in tempFoundation") + (error "AuthPlugins forced in tempFoundation") (error "PersonalisedSheetFilesSeedKey forced in tempFoundation") (error "VolatileClusterSettingsCache forced in tempFoundation") (error "AvsQuery forced in tempFoundation") + runAppLoggingT tempFoundation $ do $logInfoS "InstanceID" $ UUID.toText appInstanceID $logInfoS "Configuration" $ tshowCrop appSettings'' @@ -317,6 +325,23 @@ makeFoundation appSettings''@AppSettings{..} = do appAuthKey <- clusterSetting (Proxy :: Proxy 'ClusterAuthKey) `customRunSqlPool` sqlPool appPersonalisedSheetFilesSeedKey <- clusterSetting (Proxy :: Proxy 'ClusterPersonalisedSheetFilesSeedKey) `customRunSqlPool` sqlPool + + mAzureTenantID <- liftIO $ (fmap Text.pack) <$> lookupEnv "AZURE_ADV2_TENANT_ID" + let -- Auth Plugins + tenantID = fromMaybe (error "Tenant ID mising") mAzureTenantID + loadPlugin p prefix = do -- Loads given YesodAuthPlugin + mID <- (fmap Text.pack) <$> (lookupEnv $ prefix ++ "_CLIENT_ID") + mSecret <- (fmap Text.pack) <$> (lookupEnv $ prefix ++ "_CLIENT_SECRET") + let mArgs = (,) <$> mID <*> mSecret + guard $ isJust mArgs + return . uncurry p $ fromJust mArgs + + appAuthPlugins <- liftIO $ sequence [ + return oauth2MockServer + , loadPlugin (oauth2AzureADv2 tenantID) "AZURE_ADV2" + ] + + let appVolatileClusterSettingsCacheTime' = Clock.fromNanoSecs ns where (MkFixed ns :: Nano) = realToFrac appVolatileClusterSettingsCacheTime appVolatileClusterSettingsCache <- newTVarIO $ mkVolatileClusterSettingsCache appVolatileClusterSettingsCacheTime' @@ -376,7 +401,7 @@ makeFoundation appSettings''@AppSettings{..} = do $logDebugS "Runtime configuration" $ tshowCrop appSettings' - let foundation = mkFoundation appSettings' sqlPool smtpPool ldapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appMemcachedLocal appUploadCache appVerpSecret appAuthKey appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery + let foundation = mkFoundation appSettings' sqlPool smtpPool ldapPool appCryptoIDKey appSessionStore appSecretBoxKey appWidgetMemcached appJSONWebKeySet appClusterID appMemcached appMemcachedLocal appUploadCache appVerpSecret appAuthKey appAuthPlugins appPersonalisedSheetFilesSeedKey appVolatileClusterSettingsCache appAvsQuery -- Return the foundation $logInfoS "setup" "*** DONE ***" diff --git a/src/Auth/OAuth2.hs b/src/Auth/OAuth2.hs new file mode 100644 index 000000000..30a75a206 --- /dev/null +++ b/src/Auth/OAuth2.hs @@ -0,0 +1,58 @@ +-- SPDX-FileCopyrightText: 2023 David Mosbach +-- +-- SPDX-License-Identifier: AGPL-3.0-or-later + +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Auth.OAuth2 +( OAuthUserException(..) +, oauth2MockServer +, mockPluginName +) where + +import Data.Text + +import Import.NoFoundation + +import Yesod.Auth.OAuth2 +import Yesod.Auth.OAuth2.Prelude + + +data OAuthUserException = OAuthUserError + | OAuthUserAmbiguous -- TODO + deriving (Show, Eq, Generic) + +instance Exception OAuthUserException + +---------------------------------------- +---- OAuth2 development auth plugin ---- +---------------------------------------- + +mockPluginName :: Text +mockPluginName = "uniworx_dev" + +newtype UserID = UserID Text +instance FromJSON UserID where + parseJSON = withObject "UserID" $ \o -> + UserID <$> o .: "id" + +oauth2MockServer :: YesodAuth m => AuthPlugin m +oauth2MockServer = + let oa = OAuth2 + { oauth2ClientId = "uniworx" + , oauth2ClientSecret = Just "shh" + , oauth2AuthorizeEndpoint = fromString $ mockServerURL <> "/authorize" + , oauth2TokenEndpoint = fromString $ mockServerURL <> "/token" + , oauth2RedirectUri = Nothing + } + mockServerURL = "0.0.0.0/" + profileSrc = fromString $ mockServerURL <> "/foo" + in authOAuth2 mockPluginName oa $ \manager token -> do + (UserID userID, userResponse) <- authGetProfile mockPluginName manager token profileSrc + return Creds + { credsPlugin = mockPluginName + , credsIdent = userID + , credsExtra = setExtra token userResponse + } + + diff --git a/src/Foundation/Instances.hs b/src/Foundation/Instances.hs index b7d6a555b..0b3e23892 100644 --- a/src/Foundation/Instances.hs +++ b/src/Foundation/Instances.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Gregor Kleen ,Sarah Vaupel ,Steffen Jost ,Wolfgang Witt +-- SPDX-FileCopyrightText: 2022 Gregor Kleen ,Sarah Vaupel ,Steffen Jost ,Wolfgang Witt ,David Mosbach -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -139,9 +139,9 @@ instance YesodAuth UniWorX where setTitleI MsgLoginTitle $(widgetFile "login") - authenticate = UniWorX.authenticate + authenticate = UniWorX.oAuthenticate -- UniWorX.authenticate - authPlugins UniWorX{ appSettings' = AppSettings{..}, appLdapPool } = catMaybes + authPlugins UniWorX{ appSettings' = AppSettings{..}, appLdapPool, appAuthPlugins } = appAuthPlugins ++ catMaybes [ flip campusLogin campusUserFailoverMode <$> appLdapPool , Just . hashLogin $ pwHashAlgorithm appAuthPWHash , dummyLogin <$ guard appAuthDummyLogin diff --git a/src/Foundation/Type.hs b/src/Foundation/Type.hs index 5c77e9863..7fe72bac3 100644 --- a/src/Foundation/Type.hs +++ b/src/Foundation/Type.hs @@ -97,6 +97,7 @@ data UniWorX = UniWorX , appUploadCache :: Maybe MinioConn , appVerpSecret :: VerpSecret , appAuthKey :: Auth.Key + , appAuthPlugins :: [AuthPlugin UniWorX] , appFileSourceARC :: Maybe (ARCHandle (FileContentChunkReference, (Int, Int)) Int ByteString) , appFileSourcePrewarm :: Maybe (LRUHandle (FileContentChunkReference, (Int, Int)) UTCTime Int ByteString) , appFileInjectInhibit :: TVar (IntervalMap UTCTime (Set FileContentReference)) diff --git a/src/Foundation/Yesod/Auth.hs b/src/Foundation/Yesod/Auth.hs index efabadc80..f394ee1f3 100644 --- a/src/Foundation/Yesod/Auth.hs +++ b/src/Foundation/Yesod/Auth.hs @@ -1,9 +1,10 @@ --- SPDX-FileCopyrightText: 2022 Gregor Kleen ,Steffen Jost ,Steffen Jost +-- SPDX-FileCopyrightText: 2023 Gregor Kleen ,Steffen Jost ,Steffen Jost ,David Mosbach -- -- SPDX-License-Identifier: AGPL-3.0-or-later module Foundation.Yesod.Auth ( authenticate + , oAuthenticate , ldapLookupAndUpsert , upsertCampusUser , decodeUserTest @@ -56,6 +57,7 @@ authenticate :: ( MonadHandler m, HandlerSite m ~ UniWorX ) => Creds UniWorX -> m (AuthenticationResult UniWorX) authenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend $ do + $logErrorS "Auth" $ "\a\27[31m" <> tshow creds <> "\27[0m" now <- liftIO getCurrentTime let @@ -120,6 +122,82 @@ authenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend -> acceptExisting +-- | Authentication via OAuth 2 +oAuthenticate :: ( MonadHandler m, HandlerSite m ~ UniWorX + , YesodPersist UniWorX, BackendCompatible SqlBackend (YesodPersistBackend UniWorX) + , YesodAuth UniWorX, UserId ~ AuthId UniWorX + ) + => Creds UniWorX -> m (AuthenticationResult UniWorX) +oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend $ do + $logErrorS "Auth" $ "\a\27[31m" <> tshow creds <> "\27[0m" + now <- liftIO getCurrentTime + + let + uAuth = UniqueAuthentication $ CI.mk credsIdent + upsertMode = creds ^? _upsertCampusUserMode -- TODO adjust do OAuth + + isDummy = is (_Just . _UpsertCampusUserLoginDummy) upsertMode + isOther = is (_Just . _UpsertCampusUserLoginOther) upsertMode + + excRecovery res + | isDummy || isOther + = do + case res of + UserError err -> addMessageI Error err + ServerError err -> addMessage Error $ toHtml err + _other -> return () + acceptExisting + | otherwise + = return res + + excHandlers = + [ C.Handler $ \case + CampusUserNoResult -> do + $logWarnS "LDAP" $ "User lookup failed after successful login for " <> credsIdent + excRecovery . UserError $ IdentifierNotFound credsIdent + CampusUserAmbiguous -> do + $logWarnS "LDAP" $ "Multiple LDAP results for " <> credsIdent + excRecovery . UserError $ IdentifierNotFound credsIdent + err -> do + $logErrorS "LDAP" $ tshow err + mr <- getMessageRender + excRecovery . ServerError $ mr MsgInternalLdapError + , C.Handler $ \(cExc :: CampusUserConversionException) -> do + $logErrorS "LDAP" $ tshow cExc + mr <- getMessageRender + excRecovery . ServerError $ mr cExc + ] + + acceptExisting :: SqlPersistT (HandlerFor UniWorX) (AuthenticationResult UniWorX) + acceptExisting = do + res <- maybe (UserError $ IdentifierNotFound credsIdent) (Authenticated . entityKey) <$> getBy uAuth + case res of + Authenticated uid + -> associateUserSchoolsByTerms uid + _other + -> return () + case res of + Authenticated uid + | not isDummy -> res <$ update uid [ UserLastAuthentication =. Just now ] + _other -> return res + + $logDebugS "auth" $ tshow Creds{..} + pool <- getsYesod $ view _appLdapPool {-(case credsPlugin of + "azureadv2" -> getsYesod $ view _appLdapPool -- TODO + mockPluginName -> getsYesod $ view _appLdapPool -- TODO + _ -> error "undefined" -- TODO + )-} + flip catches excHandlers $ case pool of + Just ldapPool + | Just upsertMode' <- upsertMode -> do + ldapData <- campusUser ldapPool campusUserFailoverMode Creds{..} + $logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData + Authenticated . entityKey <$> upsertCampusUser upsertMode' ldapData + _other + -> acceptExisting + + + data CampusUserConversionException = CampusUserInvalidIdent | CampusUserInvalidEmail diff --git a/stack.yaml b/stack.yaml index 2c7b72c31..e5b66c6db 100644 --- a/stack.yaml +++ b/stack.yaml @@ -88,6 +88,9 @@ extra-deps: - yesod-eventsource - yesod-websockets + - git: https://github.com/freckle/yesod-auth-oauth2 + commit: 11948a65c405f1a99ccb327d328d416e492542a1 + - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptonite.git commit: 71a630edaf5f22c464e24fac8d9d310f4055ea1f diff --git a/stack.yaml.lock b/stack.yaml.lock index cb7c7063a..d6c9f21c4 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -5,539 +5,550 @@ packages: - completed: + commit: 22fc3bb14841d8d50997aa47f1be3852e666f787 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/encoding.git name: encoding - version: 0.8.2 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/encoding.git pantry-tree: - size: 5723 sha256: fec12328951021bb4d9326ae0b35f0c459e65f28442366efd4366cd1e18abe19 - commit: 22fc3bb14841d8d50997aa47f1be3852e666f787 + size: 5723 + version: 0.8.2 original: + commit: 22fc3bb14841d8d50997aa47f1be3852e666f787 git: https://gitlab.ifi.lmu.de/uni2work/haskell/encoding.git - commit: 22fc3bb14841d8d50997aa47f1be3852e666f787 - completed: + commit: b7071df50bad3a251a544b984e4bf98fa09b8fae + git: https://gitlab.ifi.lmu.de/uni2work/haskell/memcached-binary.git name: memcached-binary - version: 0.2.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/memcached-binary.git pantry-tree: - size: 1277 sha256: 0da0539b7b9a56d03a116dcd666bc1bbbef085659910420849484d1418aa0857 - commit: b7071df50bad3a251a544b984e4bf98fa09b8fae + size: 1277 + version: 0.2.0 original: + commit: b7071df50bad3a251a544b984e4bf98fa09b8fae git: https://gitlab.ifi.lmu.de/uni2work/haskell/memcached-binary.git - commit: b7071df50bad3a251a544b984e4bf98fa09b8fae - completed: + commit: cbea6159c2975d42f948525e03e12fc390da53c5 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/conduit-resumablesink.git name: conduit-resumablesink - version: '0.3' - git: https://gitlab.ifi.lmu.de/uni2work/haskell/conduit-resumablesink.git pantry-tree: - size: 394 sha256: 0cccf4684bbd84f81d2d3d53dd81c46cb103b5322f1d8e89e9b222211281e1b7 - commit: cbea6159c2975d42f948525e03e12fc390da53c5 + size: 394 + version: '0.3' original: + commit: cbea6159c2975d42f948525e03e12fc390da53c5 git: https://gitlab.ifi.lmu.de/uni2work/haskell/conduit-resumablesink.git - commit: cbea6159c2975d42f948525e03e12fc390da53c5 - completed: + commit: 5aa1f3b009253b02c4822005ac59ee208a10a347 + git: https://github.com/jtdaugherty/HaskellNet.git name: HaskellNet - version: 0.5.1 - git: https://github.com/jtdaugherty/HaskellNet.git pantry-tree: - size: 4011 sha256: 921b437ef18ccb04f889301c407263d6b5b72c5864803a000b1e61328988ce70 - commit: 5aa1f3b009253b02c4822005ac59ee208a10a347 + size: 4011 + version: 0.5.1 original: + commit: 5aa1f3b009253b02c4822005ac59ee208a10a347 git: https://github.com/jtdaugherty/HaskellNet.git - commit: 5aa1f3b009253b02c4822005ac59ee208a10a347 - completed: + commit: 40393c938111ac78232dc2c7eec5edb4a22d03e8 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/HaskellNet-SSL.git name: HaskellNet-SSL - version: 0.3.4.1 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/HaskellNet-SSL.git pantry-tree: - size: 841 sha256: 95dcec22fdb8af986e59f0f60aa76d4a48f34a546dca799bd571e1d183f773e0 - commit: 40393c938111ac78232dc2c7eec5edb4a22d03e8 + size: 841 + version: 0.3.4.1 original: + commit: 40393c938111ac78232dc2c7eec5edb4a22d03e8 git: https://gitlab.ifi.lmu.de/uni2work/haskell/HaskellNet-SSL.git - commit: 40393c938111ac78232dc2c7eec5edb4a22d03e8 - completed: + commit: 01afaf599ba6f8a9d804c269e91d3190b249d3f0 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/ldap-client.git name: ldap-client - version: 0.4.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/ldap-client.git pantry-tree: - size: 6176 sha256: 3fa8f102427b437b2baaec15cf884e88b47a1621b1c3fd4d8919f0263fde8656 - commit: 01afaf599ba6f8a9d804c269e91d3190b249d3f0 + size: 6176 + version: 0.4.0 original: + commit: 01afaf599ba6f8a9d804c269e91d3190b249d3f0 git: https://gitlab.ifi.lmu.de/uni2work/haskell/ldap-client.git - commit: 01afaf599ba6f8a9d804c269e91d3190b249d3f0 - completed: - subdir: serversession + commit: fda3a000f9039e35e76e28f8e88c4942fac9fd69 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git name: serversession - version: 1.0.2 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git pantry-tree: - size: 545 sha256: 83ac78a987399db3da62f84bbd335fead11aadebd57251d0688127fca984db23 - commit: fda3a000f9039e35e76e28f8e88c4942fac9fd69 - original: + size: 545 subdir: serversession - git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git + version: 1.0.2 + original: commit: fda3a000f9039e35e76e28f8e88c4942fac9fd69 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git + subdir: serversession - completed: - subdir: serversession-backend-acid-state + commit: fda3a000f9039e35e76e28f8e88c4942fac9fd69 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git name: serversession-backend-acid-state - version: 1.0.4 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git pantry-tree: - size: 544 sha256: 4804260c6245c12e1728c78dd33bf16e95b7f2b69b38b6900a4e65b1ef3e04b7 - commit: fda3a000f9039e35e76e28f8e88c4942fac9fd69 - original: + size: 544 subdir: serversession-backend-acid-state - git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git + version: 1.0.4 + original: commit: fda3a000f9039e35e76e28f8e88c4942fac9fd69 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/serversession.git + subdir: serversession-backend-acid-state - completed: + commit: dc928c3a456074b8777603bea20e81937321777f + git: https://gitlab.ifi.lmu.de/uni2work/haskell/xss-sanitize.git name: xss-sanitize - version: 0.3.6 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/xss-sanitize.git pantry-tree: - size: 750 sha256: f567a1c834448daaa164f2029fad164e6c8df2d4c92b51f811bae19cc0c95975 - commit: dc928c3a456074b8777603bea20e81937321777f + size: 750 + version: 0.3.6 original: + commit: dc928c3a456074b8777603bea20e81937321777f git: https://gitlab.ifi.lmu.de/uni2work/haskell/xss-sanitize.git - commit: dc928c3a456074b8777603bea20e81937321777f - completed: - subdir: colonnade + commit: f8170266ab25b533576e96715bedffc5aa4f19fa + git: https://gitlab.ifi.lmu.de/uni2work/haskell/colonnade.git name: colonnade - version: 1.2.0.2 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/colonnade.git pantry-tree: - size: 481 sha256: 392393652cc0f354d351482557b9385c8e6122e706359b030373656565f2e045 - commit: f8170266ab25b533576e96715bedffc5aa4f19fa - original: + size: 481 subdir: colonnade - git: https://gitlab.ifi.lmu.de/uni2work/haskell/colonnade.git + version: 1.2.0.2 + original: commit: f8170266ab25b533576e96715bedffc5aa4f19fa + git: https://gitlab.ifi.lmu.de/uni2work/haskell/colonnade.git + subdir: colonnade - completed: + commit: 42103ab247057c04c8ce7a83d9d4c160713a3df1 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/minio-hs.git name: minio-hs - version: 1.5.2 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/minio-hs.git pantry-tree: - size: 4560 sha256: c5faff15fa22a7a63f45cd903c9bd11ae03f422c26f24750f5c44cb4d0db70fc - commit: 42103ab247057c04c8ce7a83d9d4c160713a3df1 + size: 4560 + version: 1.5.2 original: + commit: 42103ab247057c04c8ce7a83d9d4c160713a3df1 git: https://gitlab.ifi.lmu.de/uni2work/haskell/minio-hs.git - commit: 42103ab247057c04c8ce7a83d9d4c160713a3df1 - completed: - subdir: cryptoids-class + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git name: cryptoids-class - version: 0.0.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git pantry-tree: - size: 412 sha256: 30466648d273ffb1d580b7961188d67a0bedb3703d6d5f8cca3c15a45295f203 - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 - original: + size: 412 subdir: cryptoids-class - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 -- completed: - subdir: cryptoids-types - name: cryptoids-types - version: 1.0.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - pantry-tree: - size: 320 - sha256: 824ac5c55c2ad553bd401bb5a99731bbdccc828ecc5d71f174e9375c4e03c46e - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 - original: - subdir: cryptoids-types - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 -- completed: - subdir: cryptoids - name: cryptoids - version: 0.5.1.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - pantry-tree: - size: 566 - sha256: b1f49dde76ff7e78b76e7f2f3b3f76c55e5e61555d1df5415ad3b6eb80dda2cb - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 - original: - subdir: cryptoids - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 -- completed: - subdir: filepath-crypto - name: filepath-crypto - version: 0.1.0.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - pantry-tree: - size: 676 - sha256: 9c31a2ffb2b1c86f9ba34eb83529c7a5a7dc68a49f89813c9b553427474654d9 - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 - original: - subdir: filepath-crypto - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 -- completed: - subdir: uuid-crypto - name: uuid-crypto - version: 1.4.0.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - pantry-tree: - size: 417 - sha256: 852e59807df1f2cf4b5a3748c46fa149d15a78651c93addfe5fc31d2d94c47f4 - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 - original: - subdir: uuid-crypto - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git - commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 -- completed: - subdir: gearhash - name: gearhash - version: 1.0.0 - git: https://github.com/gkleen/FastCDC.git - pantry-tree: - size: 551 - sha256: 89c58554f6780bff2a2cab86e94d2f562eea34e8025a9925bfdc25b56c925d3e - commit: f216e3c0a1efa11a62fd4c9c2db38f7e2b7ac72d - original: - subdir: gearhash - git: https://github.com/gkleen/FastCDC.git - commit: f216e3c0a1efa11a62fd4c9c2db38f7e2b7ac72d -- completed: - subdir: fastcdc - name: fastcdc version: 0.0.0 - git: https://github.com/gkleen/FastCDC.git + original: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + subdir: cryptoids-class +- completed: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + name: cryptoids-types + pantry-tree: + sha256: 824ac5c55c2ad553bd401bb5a99731bbdccc828ecc5d71f174e9375c4e03c46e + size: 320 + subdir: cryptoids-types + version: 1.0.0 + original: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + subdir: cryptoids-types +- completed: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + name: cryptoids + pantry-tree: + sha256: b1f49dde76ff7e78b76e7f2f3b3f76c55e5e61555d1df5415ad3b6eb80dda2cb + size: 566 + subdir: cryptoids + version: 0.5.1.0 + original: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + subdir: cryptoids +- completed: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + name: filepath-crypto + pantry-tree: + sha256: 9c31a2ffb2b1c86f9ba34eb83529c7a5a7dc68a49f89813c9b553427474654d9 + size: 676 + subdir: filepath-crypto + version: 0.1.0.0 + original: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + subdir: filepath-crypto +- completed: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + name: uuid-crypto + pantry-tree: + sha256: 852e59807df1f2cf4b5a3748c46fa149d15a78651c93addfe5fc31d2d94c47f4 + size: 417 + subdir: uuid-crypto + version: 1.4.0.0 + original: + commit: 130b0dcbf2b09ccdf387b50262f1efbbbf1819e3 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptoids.git + subdir: uuid-crypto +- completed: + commit: f216e3c0a1efa11a62fd4c9c2db38f7e2b7ac72d + git: https://github.com/gkleen/FastCDC.git + name: gearhash + pantry-tree: + sha256: 89c58554f6780bff2a2cab86e94d2f562eea34e8025a9925bfdc25b56c925d3e + size: 551 + subdir: gearhash + version: 1.0.0 + original: + commit: f216e3c0a1efa11a62fd4c9c2db38f7e2b7ac72d + git: https://github.com/gkleen/FastCDC.git + subdir: gearhash +- completed: + commit: f216e3c0a1efa11a62fd4c9c2db38f7e2b7ac72d + git: https://github.com/gkleen/FastCDC.git + name: fastcdc pantry-tree: - size: 292 sha256: aa588b55c7c9c079e39569489a8089ec312f0538d02cf0e1fffe2f0e058566b8 - commit: f216e3c0a1efa11a62fd4c9c2db38f7e2b7ac72d - original: + size: 292 subdir: fastcdc - git: https://github.com/gkleen/FastCDC.git + version: 0.0.0 + original: commit: f216e3c0a1efa11a62fd4c9c2db38f7e2b7ac72d + git: https://github.com/gkleen/FastCDC.git + subdir: fastcdc - completed: + commit: 843683d024f767de236f74d24a3348f69181a720 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/zip-stream.git name: zip-stream - version: 0.2.0.1 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/zip-stream.git pantry-tree: - size: 812 sha256: 0da8bc38d73034962d2e2d1a7586b6dee848a629319fce9cbbf578348c61118c - commit: 843683d024f767de236f74d24a3348f69181a720 + size: 812 + version: 0.2.0.1 original: + commit: 843683d024f767de236f74d24a3348f69181a720 git: https://gitlab.ifi.lmu.de/uni2work/haskell/zip-stream.git - commit: 843683d024f767de236f74d24a3348f69181a720 - completed: - subdir: yesod-core + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-core - version: 1.6.20.2 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 5954 sha256: 08c8da10b32c8d9f784238fd87232bf90b752e82f81ef2c52c62210f9aadda9a - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 5954 subdir: yesod-core - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.20.2 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-core - completed: - subdir: yesod-static + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-static - version: 1.6.1.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 2949 sha256: 32c1608243a5309005ce11e2aa379ac1d6f8c380c529785eb510770118f3da06 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 2949 subdir: yesod-static - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.1.0 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-static - completed: - subdir: yesod-persistent + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-persistent - version: 1.6.0.7 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 497 sha256: 3778ef2964e1a3890afc22cc9124eacb40e64b62bed4983a85d3b99897f54c5c - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 497 subdir: yesod-persistent - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.0.7 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-persistent - completed: - subdir: yesod-newsfeed + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-newsfeed - version: 1.7.0.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 488 sha256: 53ebad62655863a657dcf749ffd3de46f6af90dd71f55bc4d50805ac48ddb099 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 488 subdir: yesod-newsfeed - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.7.0.0 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-newsfeed - completed: - subdir: yesod-form + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-form - version: 1.7.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 1914 sha256: 260b7f16a8e1d58da137eb91aeed3a11ccbe59ba3e614457a635b9dc3e71426f - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 1914 subdir: yesod-form - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.7.0 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-form - completed: - subdir: yesod-form-multi + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-form-multi - version: 1.7.0.2 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 328 sha256: b21fc50db43733dfe6e285345856610ba4feb83329e9cf953bf8047ba18ecbd6 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 328 subdir: yesod-form-multi - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.7.0.2 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-form-multi - completed: - subdir: yesod-auth + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-auth - version: 1.6.10.3 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 1212 sha256: d335b940a207f8155f421b7146746a72d20db6ad54412154f2c829a59bf21e08 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 1212 subdir: yesod-auth - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.10.3 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-auth - completed: - subdir: yesod-auth-oauth + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-auth-oauth - version: 1.6.0.3 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 321 sha256: 39d2f7d5d1abb3a2953858c5f23880e60ecfcdad0549ddc2570204f9c47649f4 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 321 subdir: yesod-auth-oauth - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.0.3 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-auth-oauth - completed: - subdir: yesod-sitemap + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-sitemap - version: 1.6.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 314 sha256: 971f48af7011ff7816872d067e5de9cadafdd371bdf209170b77df36001abd27 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 314 subdir: yesod-sitemap - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.0 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-sitemap - completed: - subdir: yesod-test + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-test - version: 1.6.12 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 563 sha256: 3d5022e8e3f8e77abcf075c42cf49efaa26f4951159bbb5ab50b69fdfeacb7c1 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 563 subdir: yesod-test - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.12 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-test - completed: - subdir: yesod-bin + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-bin - version: 1.6.1 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 1295 sha256: 422d7816965b79826c6c24582d76dadbacd1bfb3e9a8f31208867cd788f2a5b8 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 1295 subdir: yesod-bin - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.1 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-bin - completed: - subdir: yesod + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod - version: 1.6.1.1 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 666 sha256: cb53ef3f2036185d2b4752d6fbc5d78470b4504e646e7eb4dd2397f2599daf42 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 666 subdir: yesod - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.1.1 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod - completed: - subdir: yesod-eventsource + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-eventsource - version: 1.6.0.1 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 324 sha256: 6d393201852cd024e377159ba836398e24d191563e08165430113d3c1384aff2 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 324 subdir: yesod-eventsource - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 1.6.0.1 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-eventsource - completed: - subdir: yesod-websockets + commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git name: yesod-websockets - version: 0.3.0.3 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git pantry-tree: - size: 485 sha256: 02df6117e9b74a77879ea750130ba2d8ad8d3c99e14ca678320cb578984301e5 - commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 - original: + size: 485 subdir: yesod-websockets - git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + version: 0.3.0.3 + original: commit: aa671eb41fdad360f2f7cb844f8de03479efe3f7 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/yesod.git + subdir: yesod-websockets - completed: + commit: 11948a65c405f1a99ccb327d328d416e492542a1 + git: https://github.com/freckle/yesod-auth-oauth2 + name: yesod-auth-oauth2 + pantry-tree: + sha256: a68ec51e1008c315dd15e81cc3ac1f4e2adfd3db623259395757ecae2787cef2 + size: 4277 + version: 0.7.1.3 + original: + commit: 11948a65c405f1a99ccb327d328d416e492542a1 + git: https://github.com/freckle/yesod-auth-oauth2 +- completed: + commit: 71a630edaf5f22c464e24fac8d9d310f4055ea1f + git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptonite.git name: cryptonite - version: '0.29' - git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptonite.git pantry-tree: - size: 25056 sha256: 19e49259fa5e3c257495d72b3c7c3c49537aeafd508c780c2430ddca2ef71a91 - commit: 71a630edaf5f22c464e24fac8d9d310f4055ea1f + size: 25056 + version: '0.29' original: + commit: 71a630edaf5f22c464e24fac8d9d310f4055ea1f git: https://gitlab.ifi.lmu.de/uni2work/haskell/cryptonite.git - commit: 71a630edaf5f22c464e24fac8d9d310f4055ea1f - completed: + commit: e18dd125c5ea26fa4e88bed079b61d8c1365ee37 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/esqueleto.git name: esqueleto - version: 3.5.4.0 - git: https://gitlab.ifi.lmu.de/uni2work/haskell/esqueleto.git pantry-tree: - size: 5633 sha256: 8a93dc98eb4529ff64aa5bcdaa3c00dcdf0378033ad675864e2b0fc3d869d947 - commit: e18dd125c5ea26fa4e88bed079b61d8c1365ee37 + size: 5633 + version: 3.5.4.0 original: - git: https://gitlab.ifi.lmu.de/uni2work/haskell/esqueleto.git commit: e18dd125c5ea26fa4e88bed079b61d8c1365ee37 + git: https://gitlab.ifi.lmu.de/uni2work/haskell/esqueleto.git - completed: hackage: classy-prelude-yesod-1.5.0@sha256:8f7e183bdfd6d2ea9674284c4f285294ab086aff60d9be4e5d7d2f3c1a2b05b7,1330 pantry-tree: - size: 330 sha256: ae84d4cc0e1daf985db6cdcf2ac92319531b8e60f547183cc46480d00aafbe20 + size: 330 original: hackage: classy-prelude-yesod-1.5.0@sha256:8f7e183bdfd6d2ea9674284c4f285294ab086aff60d9be4e5d7d2f3c1a2b05b7,1330 - completed: hackage: acid-state-0.16.0.1@sha256:d43f6ee0b23338758156c500290c4405d769abefeb98e9bc112780dae09ece6f,6207 pantry-tree: - size: 13678 sha256: d57bcb2ad5e01fe7424abbcf9e58cf943027b5c4a8496d93625c57b6e1272274 + size: 13678 original: hackage: acid-state-0.16.0.1@sha256:d43f6ee0b23338758156c500290c4405d769abefeb98e9bc112780dae09ece6f,6207 - completed: hackage: normaldistribution-1.1.0.3@sha256:2615b784c4112cbf6ffa0e2b55b76790290a9b9dff18a05d8c89aa374b213477,2160 pantry-tree: - size: 269 sha256: 856818862d12df8b030fa9cfef2c4ffa604d06f0eb057498db245dfffcd60e3c + size: 269 original: hackage: normaldistribution-1.1.0.3@sha256:2615b784c4112cbf6ffa0e2b55b76790290a9b9dff18a05d8c89aa374b213477,2160 - completed: hackage: pkcs7-1.0.0.1@sha256:b26e5181868667abbde3ce17f9a61cf705eb695da073cdf82e1f9dfd6cc11176,3594 pantry-tree: - size: 316 sha256: ab3c2d2880179a945ab3122c51d1657ab4a7a628292b646e047cd32b0751a80c + size: 316 original: hackage: pkcs7-1.0.0.1@sha256:b26e5181868667abbde3ce17f9a61cf705eb695da073cdf82e1f9dfd6cc11176,3594 - completed: hackage: system-locale-0.3.0.0@sha256:13b3982403d8ac8cc6138e68802be8d8e7cf7ebc4cbc7e47e99e3c0dd1be066a,1529 pantry-tree: - size: 446 sha256: 3b22af3e6315835bf614a0d30381ec7e47aca147b59ba601aeaa26f1fdc19373 + size: 446 original: hackage: system-locale-0.3.0.0@sha256:13b3982403d8ac8cc6138e68802be8d8e7cf7ebc4cbc7e47e99e3c0dd1be066a,1529 - completed: hackage: token-bucket-0.1.0.1@sha256:d8e85f2fc373939975e7ace7907baee177531ab6e43df94e330a2357e64a2d11,1899 pantry-tree: - size: 399 sha256: b0b4a08ea1bf76bd108310f64d7f80e0f30b61ddc3d71f6cab7bdce329d2c1fa + size: 399 original: hackage: token-bucket-0.1.0.1@sha256:d8e85f2fc373939975e7ace7907baee177531ab6e43df94e330a2357e64a2d11,1899 - completed: hackage: tz-0.1.3.5@sha256:fb17ca50a7d943e511c0ca70342dc83f66aa2532de2745632f1f5f9b1ad783c4,5086 pantry-tree: - size: 1179 sha256: 6482698ea1b1a93bd684fca35836b35e8cdf53fe51b0fa6b215afa7da1f983a6 + size: 1179 original: hackage: tz-0.1.3.5@sha256:fb17ca50a7d943e511c0ca70342dc83f66aa2532de2745632f1f5f9b1ad783c4,5086 - completed: hackage: unidecode-0.1.0.4@sha256:99581ee1ea334a4596a09ae3642e007808457c66893b587e965b31f15cbf8c4d,1144 pantry-tree: - size: 492 sha256: 4959068a0caf410dd4b8046f0b0138e3cf6471abb0cc865c9993db3b2930d283 + size: 492 original: hackage: unidecode-0.1.0.4@sha256:99581ee1ea334a4596a09ae3642e007808457c66893b587e965b31f15cbf8c4d,1144 - completed: hackage: hlint-test-0.1.0.0@sha256:e427c0593433205fc629fb05b74c6b1deb1de72d1571f26142de008f0d5ee7a9,1814 pantry-tree: - size: 442 sha256: 347eac6c8a3c02fc0101444d6526b57b3c27785809149b12f90d8db57c721fea + size: 442 original: hackage: hlint-test-0.1.0.0@sha256:e427c0593433205fc629fb05b74c6b1deb1de72d1571f26142de008f0d5ee7a9,1814 - completed: hackage: servant-quickcheck-0.0.10.0@sha256:1d5849d703c2487752f8fc7391cca7c998ee24f54ca0bb72d238bf99b64ac667,3755 pantry-tree: - size: 976 sha256: 37dab60111c71d011fc4964e9a8b4b05ac544bc0ba8155e895518680066c2adb + size: 976 original: hackage: servant-quickcheck-0.0.10.0@sha256:1d5849d703c2487752f8fc7391cca7c998ee24f54ca0bb72d238bf99b64ac667,3755 - completed: hackage: servant-flatten-0.2@sha256:276896f7c5cdec5b8f8493f6205fded0cc602d050b58fdb09a6d7c85c3bb0837,1234 pantry-tree: - size: 325 sha256: 04f12c7bef2c3f9a25d94eb9489752ed498db8e243069fe95838dbb51df1dcb3 + size: 325 original: hackage: servant-flatten-0.2@sha256:276896f7c5cdec5b8f8493f6205fded0cc602d050b58fdb09a6d7c85c3bb0837,1234 - completed: hackage: network-arbitrary-0.7.0.0@sha256:0cd381c80ae20c16048936edcdb018b1d9fbe2b6ac8c44e908df403a5c6d7cd5,2520 pantry-tree: - size: 912 sha256: a40b62eddfb12cfec753a10836a4ef5fe8ec94d7478e6957e1fe5729017928fb + size: 912 original: hackage: network-arbitrary-0.7.0.0@sha256:0cd381c80ae20c16048936edcdb018b1d9fbe2b6ac8c44e908df403a5c6d7cd5,2520 - completed: hackage: saltine-0.2.0.0@sha256:2232a285ef326b0942bbcbfa6f465933a020f27e19552213e688fe371d66dddd,5198 pantry-tree: - size: 5016 sha256: fdf4397f4b1ed7975f38d0b463eb6c9d206d0c85d157c41c19983e80b2005763 + size: 5016 original: hackage: saltine-0.2.0.0@sha256:2232a285ef326b0942bbcbfa6f465933a020f27e19552213e688fe371d66dddd,5198 - completed: hackage: persistent-postgresql-2.13.0.3@sha256:43384bf8ed9c931c673e6abb763c8811113d1b7004095faaae1eb42e2cd52d8f,3601 pantry-tree: - size: 1059 sha256: 2d647a17372e42bc54331cfb35f5a55a71e6854dac8299b7ed6a1c69ae12734d + size: 1059 original: hackage: persistent-postgresql-2.13.0.3@sha256:43384bf8ed9c931c673e6abb763c8811113d1b7004095faaae1eb42e2cd52d8f,3601 snapshots: - completed: + sha256: c632012da648385b9fa3c29f4e0afd56ead299f1c5528ee789058be410e883c0 size: 585393 url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/18/0.yaml - sha256: c632012da648385b9fa3c29f4e0afd56ead299f1c5528ee789058be410e883c0 original: lts-18.0 From 9b9370fed0f55098163b55d88aea5fd55ffd736c Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Sun, 3 Dec 2023 15:06:39 +0000 Subject: [PATCH 2/4] feat(auth): WIP authorization function --- src/Auth/OAuth2.hs | 9 ++--- src/Foundation/Types.hs | 18 +++++++++- src/Foundation/Yesod/Auth.hs | 67 +++++++++++++++++++++++++----------- 3 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/Auth/OAuth2.hs b/src/Auth/OAuth2.hs index 30a75a206..9b4efdd5d 100644 --- a/src/Auth/OAuth2.hs +++ b/src/Auth/OAuth2.hs @@ -5,7 +5,7 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Auth.OAuth2 -( OAuthUserException(..) +( AzureUserException(..) , oauth2MockServer , mockPluginName ) where @@ -18,11 +18,12 @@ import Yesod.Auth.OAuth2 import Yesod.Auth.OAuth2.Prelude -data OAuthUserException = OAuthUserError - | OAuthUserAmbiguous -- TODO +data AzureUserException = AzureUserError + | AzureUserNoResult + | AzureUserAmbiguous -- TODO deriving (Show, Eq, Generic) -instance Exception OAuthUserException +instance Exception AzureUserException ---------------------------------------- ---- OAuth2 development auth plugin ---- diff --git a/src/Foundation/Types.hs b/src/Foundation/Types.hs index 786b943b0..252c1be26 100644 --- a/src/Foundation/Types.hs +++ b/src/Foundation/Types.hs @@ -1,4 +1,4 @@ --- SPDX-FileCopyrightText: 2022 Gregor Kleen +-- SPDX-FileCopyrightText: 2023 Gregor Kleen ,David Mosbach -- -- SPDX-License-Identifier: AGPL-3.0-or-later @@ -6,6 +6,9 @@ module Foundation.Types ( UpsertCampusUserMode(..) , _UpsertCampusUserLoginLdap, _UpsertCampusUserLoginDummy, _UpsertCampusUserLoginOther, _UpsertCampusUserLdapSync, _UpsertCampusUserGuessUser , _upsertCampusUserIdent + , UpsertAzureUserMode(..) + , _UpsertAzureUserLoginOAuth, _UpsertAzureUserLoginDummy, _UpsertAzureUserLoginOther, _UpsertAzureUserOAuthSync, _UpsertAzureUserGuessUser + , _upsertAzureUserIdent ) where import Import.NoFoundation @@ -21,3 +24,16 @@ data UpsertCampusUserMode makeLenses_ ''UpsertCampusUserMode makePrisms ''UpsertCampusUserMode + + +-- Azure users logging in via OAuth2 +data UpsertAzureUserMode + = UpsertAzureUserLoginOAuth + | UpsertAzureUserLoginDummy { upsertAzureUserIdent :: UserIdent } + | UpsertAzureUserLoginOther { upsertAzureUserIdent :: UserIdent } + | UpsertAzureUserOAuthSync { upsertAzureUserIdent :: UserIdent } + | UpsertAzureUserGuessUser + deriving (Eq, Ord, Read, Show, Generic) + +makeLenses_ ''UpsertAzureUserMode +makePrisms ''UpsertAzureUserMode diff --git a/src/Foundation/Yesod/Auth.hs b/src/Foundation/Yesod/Auth.hs index f394ee1f3..d01605495 100644 --- a/src/Foundation/Yesod/Auth.hs +++ b/src/Foundation/Yesod/Auth.hs @@ -25,6 +25,7 @@ import Foundation.Authorization (AuthorizationCacheKey(..)) import Yesod.Auth.Message import Auth.LDAP +import Auth.OAuth2 import Auth.PWHash (apHash) import Auth.Dummy (apDummy) @@ -122,22 +123,22 @@ authenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend -> acceptExisting --- | Authentication via OAuth 2 +-- | Authentication via AzureADv2 / OAuth 2 oAuthenticate :: ( MonadHandler m, HandlerSite m ~ UniWorX , YesodPersist UniWorX, BackendCompatible SqlBackend (YesodPersistBackend UniWorX) , YesodAuth UniWorX, UserId ~ AuthId UniWorX ) => Creds UniWorX -> m (AuthenticationResult UniWorX) oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend $ do - $logErrorS "Auth" $ "\a\27[31m" <> tshow creds <> "\27[0m" + $logErrorS "OAuth" $ "\a\27[31m" <> tshow creds <> "\27[0m" now <- liftIO getCurrentTime let uAuth = UniqueAuthentication $ CI.mk credsIdent - upsertMode = creds ^? _upsertCampusUserMode -- TODO adjust do OAuth + upsertMode = creds ^? _upsertAzureUserMode - isDummy = is (_Just . _UpsertCampusUserLoginDummy) upsertMode - isOther = is (_Just . _UpsertCampusUserLoginOther) upsertMode + isDummy = is (_Just . _UpsertAzureUserLoginDummy) upsertMode -- mock server + isOther = is (_Just . _UpsertAzureUserLoginOther) upsertMode excRecovery res | isDummy || isOther @@ -152,18 +153,18 @@ oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend excHandlers = [ C.Handler $ \case - CampusUserNoResult -> do - $logWarnS "LDAP" $ "User lookup failed after successful login for " <> credsIdent + AzureUserNoResult -> do + $logWarnS "OAuth" $ "User lookup failed after successful login for " <> credsIdent excRecovery . UserError $ IdentifierNotFound credsIdent - CampusUserAmbiguous -> do - $logWarnS "LDAP" $ "Multiple LDAP results for " <> credsIdent + AzureUserAmbiguous -> do + $logWarnS "OAuth" $ "Multiple OAuth results for " <> credsIdent excRecovery . UserError $ IdentifierNotFound credsIdent err -> do - $logErrorS "LDAP" $ tshow err + $logErrorS "OAuth" $ tshow err mr <- getMessageRender - excRecovery . ServerError $ mr MsgInternalLdapError - , C.Handler $ \(cExc :: CampusUserConversionException) -> do - $logErrorS "LDAP" $ tshow cExc + excRecovery . ServerError $ mr MsgInternalLdapError -- TODO where does this come from? + , C.Handler $ \(cExc :: CampusUserConversionException) -> do -- TODO new exception type or not? + $logErrorS "OAuth" $ tshow cExc mr <- getMessageRender excRecovery . ServerError $ mr cExc ] @@ -181,12 +182,15 @@ oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend | not isDummy -> res <$ update uid [ UserLastAuthentication =. Just now ] _other -> return res - $logDebugS "auth" $ tshow Creds{..} - pool <- getsYesod $ view _appLdapPool {-(case credsPlugin of - "azureadv2" -> getsYesod $ view _appLdapPool -- TODO - mockPluginName -> getsYesod $ view _appLdapPool -- TODO - _ -> error "undefined" -- TODO - )-} + $logDebugS "oauth" $ tshow Creds{..} + -- TODO look user up in DB + -- If not in DB then put (maybe prompt for email) + -- If in DB but first time oauth then prompt for password & update entry + -- Now user should be in DB -> authenticated + flip catches excHandlers $ case upsertMode of + Just upsertMode' -> error $ show upsertMode' --TODO + Nothing -> error "nothing" --TODO + {-pool <- getsYesod $ view _appLdapPool flip catches excHandlers $ case pool of Just ldapPool | Just upsertMode' <- upsertMode -> do @@ -194,7 +198,7 @@ oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend $logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData Authenticated . entityKey <$> upsertCampusUser upsertMode' ldapData _other - -> acceptExisting + -> acceptExisting-} @@ -231,6 +235,29 @@ _upsertCampusUserMode mMode cs@Creds{..} defaultOther = apHash + +_upsertAzureUserMode :: Traversal' (Creds UniWorX) UpsertAzureUserMode +_upsertAzureUserMode mMode cs@Creds{..} + | credsPlugin == mockPluginName = setMode <$> mMode (UpsertAzureUserLoginDummy $ CI.mk credsIdent) + | credsPlugin == "azureadv2" = setMode <$> mMode UpsertAzureUserLoginOAuth + | otherwise = setMode <$> mMode (UpsertAzureUserLoginOther $ CI.mk credsIdent) + where + setMode UpsertAzureUserLoginOAuth + = cs{ credsPlugin = "azureadv2" } + setMode (UpsertAzureUserLoginDummy ident) + = cs{ credsPlugin = mockPluginName + , credsIdent = CI.original ident + } + setMode (UpsertAzureUserLoginOther ident) + = cs{ credsPlugin = bool defaultOther credsPlugin (credsPlugin /= mockPluginName && credsPlugin /= "azureadv2") + , credsIdent = CI.original ident + } + setMode _ = cs + + defaultOther = apHash + + + ldapLookupAndUpsert :: forall m. (MonadHandler m, HandlerSite m ~ UniWorX, MonadMask m, MonadUnliftIO m) => Text -> SqlPersistT m (Entity User) ldapLookupAndUpsert ident = getsYesod (view _appLdapPool) >>= \case From 44d082f8b95ad1b2d1ee0e9ce71d84dfbcd23df4 Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Sun, 3 Dec 2023 23:23:44 +0000 Subject: [PATCH 3/4] feat(auth): added azure & mock server to login widget --- src/Application.hs | 6 +++--- templates/login.hamlet | 10 +++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Application.hs b/src/Application.hs index c6f2cb68c..8b9a21739 100644 --- a/src/Application.hs +++ b/src/Application.hs @@ -326,12 +326,12 @@ makeFoundation appSettings''@AppSettings{..} = do appPersonalisedSheetFilesSeedKey <- clusterSetting (Proxy :: Proxy 'ClusterPersonalisedSheetFilesSeedKey) `customRunSqlPool` sqlPool - mAzureTenantID <- liftIO $ (fmap Text.pack) <$> lookupEnv "AZURE_ADV2_TENANT_ID" + mAzureTenantID <- liftIO $ (fmap Text.pack) <$> (return $ Just "123") -- lookupEnv "AZURE_ADV2_TENANT_ID" let -- Auth Plugins tenantID = fromMaybe (error "Tenant ID mising") mAzureTenantID loadPlugin p prefix = do -- Loads given YesodAuthPlugin - mID <- (fmap Text.pack) <$> (lookupEnv $ prefix ++ "_CLIENT_ID") - mSecret <- (fmap Text.pack) <$> (lookupEnv $ prefix ++ "_CLIENT_SECRET") + mID <- (fmap Text.pack) <$> (return $ Just "UWX") -- (lookupEnv $ prefix ++ "_CLIENT_ID") + mSecret <- (fmap Text.pack) <$> (return $ Just prefix) -- (lookupEnv $ prefix ++ "_CLIENT_SECRET") let mArgs = (,) <$> mID <*> mSecret guard $ isJust mArgs return . uncurry p $ fromJust mArgs diff --git a/templates/login.hamlet b/templates/login.hamlet index 19539af3f..7c1483d65 100644 --- a/templates/login.hamlet +++ b/templates/login.hamlet @@ -5,7 +5,15 @@ $# $# SPDX-License-Identifier: AGPL-3.0-or-later $forall AuthPlugin{apName, apLogin} <- plugins - $if apName == "LDAP" + $if apName == "azureadv2" +
+

Azure + ^{apLogin toParent} + $elseif apName == "uniworx_dev" +
+

_{MsgDummyLoginTitle} + ^{apLogin toParent} + $elseif apName == "LDAP"

_{MsgLDAPLoginTitle} ^{apLogin toParent} From cf89722c7fd47c0d0202bbaf44779ca847f18c61 Mon Sep 17 00:00:00 2001 From: David Mosbach Date: Mon, 4 Dec 2023 00:32:01 +0000 Subject: [PATCH 4/4] chore(auth): enabled ldap lookup for oauth2 creds --- src/Foundation/Instances.hs | 4 +++- src/Foundation/Yesod/Auth.hs | 36 +++++++++++++++++++++++------------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/Foundation/Instances.hs b/src/Foundation/Instances.hs index 0b3e23892..79fefdccf 100644 --- a/src/Foundation/Instances.hs +++ b/src/Foundation/Instances.hs @@ -139,7 +139,9 @@ instance YesodAuth UniWorX where setTitleI MsgLoginTitle $(widgetFile "login") - authenticate = UniWorX.oAuthenticate -- UniWorX.authenticate + authenticate c@Creds{..} + | credsPlugin `elem` ["azureadv2", "uniworx_dev"] = UniWorX.oAuthenticate c + | otherwise = UniWorX.authenticate c authPlugins UniWorX{ appSettings' = AppSettings{..}, appLdapPool, appAuthPlugins } = appAuthPlugins ++ catMaybes [ flip campusLogin campusUserFailoverMode <$> appLdapPool diff --git a/src/Foundation/Yesod/Auth.hs b/src/Foundation/Yesod/Auth.hs index d01605495..2bd046479 100644 --- a/src/Foundation/Yesod/Auth.hs +++ b/src/Foundation/Yesod/Auth.hs @@ -182,23 +182,17 @@ oAuthenticate creds@Creds{..} = liftHandler . runDB . withReaderT projectBackend | not isDummy -> res <$ update uid [ UserLastAuthentication =. Just now ] _other -> return res - $logDebugS "oauth" $ tshow Creds{..} - -- TODO look user up in DB - -- If not in DB then put (maybe prompt for email) - -- If in DB but first time oauth then prompt for password & update entry - -- Now user should be in DB -> authenticated - flip catches excHandlers $ case upsertMode of - Just upsertMode' -> error $ show upsertMode' --TODO - Nothing -> error "nothing" --TODO - {-pool <- getsYesod $ view _appLdapPool + $logDebugS "oauth" $ tshow creds + -- TODO If user not in DB then put + pool <- getsYesod $ view _appLdapPool flip catches excHandlers $ case pool of Just ldapPool | Just upsertMode' <- upsertMode -> do - ldapData <- campusUser ldapPool campusUserFailoverMode Creds{..} - $logDebugS "LDAP" $ "Successful LDAP lookup: " <> tshow ldapData - Authenticated . entityKey <$> upsertCampusUser upsertMode' ldapData + ldapData <- campusUser ldapPool campusUserFailoverMode creds + $logDebugS "OAuth" $ "Successful LDAP lookup of Azure user: " <> tshow ldapData + Authenticated . entityKey <$> upsertAzureUser upsertMode' ldapData _other - -> acceptExisting-} + -> acceptExisting @@ -267,6 +261,22 @@ ldapLookupAndUpsert ident = Nothing -> throwM CampusUserNoResult Just ldapResponse -> upsertCampusUser UpsertCampusUserGuessUser ldapResponse + +upsertAzureUser :: forall m. + ( MonadHandler m, HandlerSite m ~ UniWorX + , MonadCatch m + ) + => UpsertAzureUserMode -> Ldap.AttrList [] -> SqlPersistT m (Entity User) -- TODO UpsertAzureUserMode is probably redundant +upsertAzureUser upsertMode = upsertCampusUser (toCampus upsertMode) + where + toCampus :: UpsertAzureUserMode -> UpsertCampusUserMode + toCampus UpsertAzureUserLoginOAuth = UpsertCampusUserLoginLdap + toCampus (UpsertAzureUserLoginDummy u) = UpsertCampusUserLoginDummy u + toCampus (UpsertAzureUserLoginOther u) = UpsertCampusUserLoginOther u + toCampus (UpsertAzureUserOAuthSync u) = UpsertCampusUserLdapSync u + toCampus UpsertAzureUserGuessUser = UpsertCampusUserGuessUser + + {- THIS FUNCION JUST DECODES, BUT IT DOES NOT QUERY LDAP! upsertCampusUserByCn :: forall m. ( MonadHandler m, HandlerSite m ~ UniWorX