diff --git a/next/prisma/schema.prisma b/next/prisma/schema.prisma index 315022190c..49c9662a17 100644 --- a/next/prisma/schema.prisma +++ b/next/prisma/schema.prisma @@ -99,6 +99,39 @@ model VerificationToken { @@unique([identifier, token]) } +model OAuthCredentials { + id String @id @default(cuid()) + installation_id String + provider String + token_type String + access_token String + scope String? + data Json + + create_date DateTime @default(now()) + update_date DateTime? @updatedAt + delete_date DateTime? + + @@unique([installation_id]) + @@map("oauth_credentials") +} + +model OAuthInstallation { + id String @id @default(cuid()) + user_id String + organization_id String? + provider String + state String + + create_date DateTime @default(now()) + update_date DateTime? @updatedAt + delete_date DateTime? + + @@unique([user_id, organization_id, provider]) + @@index([state]) + @@map("oauth_installation") +} + model Agent { id String @id @default(cuid()) userId String diff --git a/platform/poetry.lock b/platform/poetry.lock index 04cb86f5da..c56837f704 100644 --- a/platform/poetry.lock +++ b/platform/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.5.0 and should not be changed by hand. [[package]] name = "aiohttp" @@ -530,18 +530,88 @@ files = [ [[package]] name = "charset-normalizer" -version = "2.0.12" +version = "3.2.0" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false -python-versions = ">=3.5.0" +python-versions = ">=3.7.0" files = [ - {file = "charset-normalizer-2.0.12.tar.gz", hash = "sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597"}, - {file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"}, + {file = "charset-normalizer-3.2.0.tar.gz", hash = "sha256:3bb3d25a8e6c0aedd251753a79ae98a093c7e7b471faa3aa9a93a81431987ace"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:0b87549028f680ca955556e3bd57013ab47474c3124dc069faa0b6545b6c9710"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7c70087bfee18a42b4040bb9ec1ca15a08242cf5867c58726530bdf3945672ed"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a103b3a7069b62f5d4890ae1b8f0597618f628b286b03d4bc9195230b154bfa9"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94aea8eff76ee6d1cdacb07dd2123a68283cb5569e0250feab1240058f53b623"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db901e2ac34c931d73054d9797383d0f8009991e723dab15109740a63e7f902a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b0dac0ff919ba34d4df1b6131f59ce95b08b9065233446be7e459f95554c0dc8"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:193cbc708ea3aca45e7221ae58f0fd63f933753a9bfb498a3b474878f12caaad"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:09393e1b2a9461950b1c9a45d5fd251dc7c6f228acab64da1c9c0165d9c7765c"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:baacc6aee0b2ef6f3d308e197b5d7a81c0e70b06beae1f1fcacffdbd124fe0e3"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:bf420121d4c8dce6b889f0e8e4ec0ca34b7f40186203f06a946fa0276ba54029"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:c04a46716adde8d927adb9457bbe39cf473e1e2c2f5d0a16ceb837e5d841ad4f"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:aaf63899c94de41fe3cf934601b0f7ccb6b428c6e4eeb80da72c58eab077b19a"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:d62e51710986674142526ab9f78663ca2b0726066ae26b78b22e0f5e571238dd"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win32.whl", hash = "sha256:04e57ab9fbf9607b77f7d057974694b4f6b142da9ed4a199859d9d4d5c63fe96"}, + {file = "charset_normalizer-3.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:48021783bdf96e3d6de03a6e39a1171ed5bd7e8bb93fc84cc649d11490f87cea"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4957669ef390f0e6719db3613ab3a7631e68424604a7b448f079bee145da6e09"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:46fb8c61d794b78ec7134a715a3e564aafc8f6b5e338417cb19fe9f57a5a9bf2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f779d3ad205f108d14e99bb3859aa7dd8e9c68874617c72354d7ecaec2a054ac"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f25c229a6ba38a35ae6e25ca1264621cc25d4d38dca2942a7fce0b67a4efe918"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2efb1bd13885392adfda4614c33d3b68dee4921fd0ac1d3988f8cbb7d589e72a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1f30b48dd7fa1474554b0b0f3fdfdd4c13b5c737a3c6284d3cdc424ec0ffff3a"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:246de67b99b6851627d945db38147d1b209a899311b1305dd84916f2b88526c6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9bd9b3b31adcb054116447ea22caa61a285d92e94d710aa5ec97992ff5eb7cf3"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:8c2f5e83493748286002f9369f3e6607c565a6a90425a3a1fef5ae32a36d749d"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:3170c9399da12c9dc66366e9d14da8bf7147e1e9d9ea566067bbce7bb74bd9c2"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:7a4826ad2bd6b07ca615c74ab91f32f6c96d08f6fcc3902ceeedaec8cdc3bcd6"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:3b1613dd5aee995ec6d4c69f00378bbd07614702a315a2cf6c1d21461fe17c23"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9e608aafdb55eb9f255034709e20d5a83b6d60c054df0802fa9c9883d0a937aa"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win32.whl", hash = "sha256:f2a1d0fd4242bd8643ce6f98927cf9c04540af6efa92323e9d3124f57727bfc1"}, + {file = "charset_normalizer-3.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:681eb3d7e02e3c3655d1b16059fbfb605ac464c834a0c629048a30fad2b27489"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c57921cda3a80d0f2b8aec7e25c8aa14479ea92b5b51b6876d975d925a2ea346"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:41b25eaa7d15909cf3ac4c96088c1f266a9a93ec44f87f1d13d4a0e86c81b982"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f058f6963fd82eb143c692cecdc89e075fa0828db2e5b291070485390b2f1c9c"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a7647ebdfb9682b7bb97e2a5e7cb6ae735b1c25008a70b906aecca294ee96cf4"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eef9df1eefada2c09a5e7a40991b9fc6ac6ef20b1372abd48d2794a316dc0449"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e03b8895a6990c9ab2cdcd0f2fe44088ca1c65ae592b8f795c3294af00a461c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:ee4006268ed33370957f55bf2e6f4d263eaf4dc3cfc473d1d90baff6ed36ce4a"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c4983bf937209c57240cff65906b18bb35e64ae872da6a0db937d7b4af845dd7"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:3bb7fda7260735efe66d5107fb7e6af6a7c04c7fce9b2514e04b7a74b06bf5dd"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:72814c01533f51d68702802d74f77ea026b5ec52793c791e2da806a3844a46c3"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:70c610f6cbe4b9fce272c407dd9d07e33e6bf7b4aa1b7ffb6f6ded8e634e3592"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win32.whl", hash = "sha256:a401b4598e5d3f4a9a811f3daf42ee2291790c7f9d74b18d75d6e21dda98a1a1"}, + {file = "charset_normalizer-3.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c0b21078a4b56965e2b12f247467b234734491897e99c1d51cee628da9786959"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:95eb302ff792e12aba9a8b8f8474ab229a83c103d74a750ec0bd1c1eea32e669"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a100c6d595a7f316f1b6f01d20815d916e75ff98c27a01ae817439ea7726329"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:6339d047dab2780cc6220f46306628e04d9750f02f983ddb37439ca47ced7149"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4b749b9cc6ee664a3300bb3a273c1ca8068c46be705b6c31cf5d276f8628a94"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a38856a971c602f98472050165cea2cdc97709240373041b69030be15047691f"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f87f746ee241d30d6ed93969de31e5ffd09a2961a051e60ae6bddde9ec3583aa"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89f1b185a01fe560bc8ae5f619e924407efca2191b56ce749ec84982fc59a32a"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e1c8a2f4c69e08e89632defbfabec2feb8a8d99edc9f89ce33c4b9e36ab63037"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f4ac36d8e2b4cc1aa71df3dd84ff8efbe3bfb97ac41242fbcfc053c67434f46"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:a386ebe437176aab38c041de1260cd3ea459c6ce5263594399880bbc398225b2"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:ccd16eb18a849fd8dcb23e23380e2f0a354e8daa0c984b8a732d9cfaba3a776d"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:e6a5bf2cba5ae1bb80b154ed68a3cfa2fa00fde979a7f50d6598d3e17d9ac20c"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:45de3f87179c1823e6d9e32156fb14c1927fcc9aba21433f088fdfb555b77c10"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win32.whl", hash = "sha256:1000fba1057b92a65daec275aec30586c3de2401ccdcd41f8a5c1e2c87078706"}, + {file = "charset_normalizer-3.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:8b2c760cfc7042b27ebdb4a43a4453bd829a5742503599144d54a032c5dc7e9e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:855eafa5d5a2034b4621c74925d89c5efef61418570e5ef9b37717d9c796419c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:203f0c8871d5a7987be20c72442488a0b8cfd0f43b7973771640fc593f56321f"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e857a2232ba53ae940d3456f7533ce6ca98b81917d47adc3c7fd55dad8fab858"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e86d77b090dbddbe78867a0275cb4df08ea195e660f1f7f13435a4649e954e5"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c4fb39a81950ec280984b3a44f5bd12819953dc5fa3a7e6fa7a80db5ee853952"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2dee8e57f052ef5353cf608e0b4c871aee320dd1b87d351c28764fc0ca55f9f4"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8700f06d0ce6f128de3ccdbc1acaea1ee264d2caa9ca05daaf492fde7c2a7200"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1920d4ff15ce893210c1f0c0e9d19bfbecb7983c76b33f046c13a8ffbd570252"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:c1c76a1743432b4b60ab3358c937a3fe1341c828ae6194108a94c69028247f22"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f7560358a6811e52e9c4d142d497f1a6e10103d3a6881f18d04dbce3729c0e2c"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c8063cf17b19661471ecbdb3df1c84f24ad2e389e326ccaf89e3fb2484d8dd7e"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:cd6dbe0238f7743d0efe563ab46294f54f9bc8f4b9bcf57c3c666cc5bc9d1299"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1249cbbf3d3b04902ff081ffbb33ce3377fa6e4c7356f759f3cd076cc138d020"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win32.whl", hash = "sha256:6c409c0deba34f147f77efaa67b8e4bb83d2f11c8806405f76397ae5b8c0d1c9"}, + {file = "charset_normalizer-3.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:7095f6fbfaa55defb6b733cfeb14efaae7a29f0b59d8cf213be4e7ca0b857b80"}, + {file = "charset_normalizer-3.2.0-py3-none-any.whl", hash = "sha256:8e098148dd37b4ce3baca71fb394c81dc5d9c7728c95df695d2dca218edf40e6"}, ] -[package.extras] -unicode-backport = ["unicodedata2"] - [[package]] name = "click" version = "8.1.6" @@ -2967,24 +3037,24 @@ renderpm = ["rl-renderPM (>=4.0.3,<4.1)"] [[package]] name = "requests" -version = "2.28.0" +version = "2.31.0" description = "Python HTTP for Humans." optional = false -python-versions = ">=3.7, <4" +python-versions = ">=3.7" files = [ - {file = "requests-2.28.0-py3-none-any.whl", hash = "sha256:bc7861137fbce630f17b03d3ad02ad0bf978c844f3536d0edda6499dafce2b6f"}, - {file = "requests-2.28.0.tar.gz", hash = "sha256:d568723a7ebd25875d8d1eaf5dfa068cd2fc8194b2e483d7b1f7c81918dbec6b"}, + {file = "requests-2.31.0-py3-none-any.whl", hash = "sha256:58cd2187c01e70e6e26505bca751777aa9f2ee0b7f4300988b709f44e013003f"}, + {file = "requests-2.31.0.tar.gz", hash = "sha256:942c5a758f98d790eaed1a29cb6eefc7ffb0d1cf7af05c3d2791656dbd6ad1e1"}, ] [package.dependencies] certifi = ">=2017.4.17" -charset-normalizer = ">=2.0.0,<2.1.0" +charset-normalizer = ">=2,<4" idna = ">=2.5,<4" -urllib3 = ">=1.21.1,<1.27" +urllib3 = ">=1.21.1,<3" [package.extras] socks = ["PySocks (>=1.5.6,!=1.5.7)"] -use-chardet-on-py3 = ["chardet (>=3.0.2,<5)"] +use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "restructuredtext-lint" @@ -3103,6 +3173,21 @@ files = [ {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] +[[package]] +name = "slack-sdk" +version = "3.21.3" +description = "The Slack API Platform SDK for Python" +optional = false +python-versions = ">=3.6.0" +files = [ + {file = "slack_sdk-3.21.3-py2.py3-none-any.whl", hash = "sha256:de3c07b92479940b61cd68c566f49fbc9974c8f38f661d26244078f3903bb9cc"}, + {file = "slack_sdk-3.21.3.tar.gz", hash = "sha256:20829bdc1a423ec93dac903470975ebf3bc76fd3fd91a4dadc0eeffc940ecb0c"}, +] + +[package.extras] +optional = ["SQLAlchemy (>=1.4,<3)", "aiodns (>1.0)", "aiohttp (>=3.7.3,<4)", "boto3 (<=2)", "websocket-client (>=1,<2)", "websockets (>=10,<11)"] +testing = ["Flask (>=1,<2)", "Flask-Sockets (>=0.2,<1)", "Jinja2 (==3.0.3)", "Werkzeug (<2)", "black (==22.8.0)", "boto3 (<=2)", "click (==8.0.4)", "databases (>=0.5)", "flake8 (>=5,<6)", "itsdangerous (==1.1.0)", "moto (>=3,<4)", "psutil (>=5,<6)", "pytest (>=6.2.5,<7)", "pytest-asyncio (<1)", "pytest-cov (>=2,<3)"] + [[package]] name = "smmap" version = "5.0.0" @@ -3934,4 +4019,4 @@ multidict = ">=4.0" [metadata] lock-version = "2.0" python-versions = "^3.9" -content-hash = "468f853a9b80a4f7e2f0c4b7c18a1757623f7cc47a23e92c8b2018b7d28d0a81" +content-hash = "f405569acfbbf53537c6852668905595a7c99b1b025fe904cd07e3a47dacbe15" diff --git a/platform/pyproject.toml b/platform/pyproject.toml index 0c0a72702b..54bc0e83c2 100644 --- a/platform/pyproject.toml +++ b/platform/pyproject.toml @@ -25,7 +25,7 @@ httptools = "^0.5.0" sentry-sdk = "^1.28.1" loguru = "^0.7.0" aiokafka = "^0.8.1" -requests = "2.28.0" +requests = "^2.31.0" langchain = "0.0.218" openai = "^0.27.8" wikipedia = "^1.4.0" @@ -47,6 +47,7 @@ aws-secretsmanager-caching = "^1.1.1.5" botocore = "^1.29.153" stripe = "^5.4.0" tabula-py = "^2.7.0" +slack-sdk = "^3.21.3" [tool.poetry.dev-dependencies] autopep8 = "^2.0.2" diff --git a/platform/reworkd_platform/db/crud/base.py b/platform/reworkd_platform/db/crud/base.py index f6843c408a..3eebc0c181 100644 --- a/platform/reworkd_platform/db/crud/base.py +++ b/platform/reworkd_platform/db/crud/base.py @@ -1,5 +1,9 @@ +from typing import TypeVar + from sqlalchemy.ext.asyncio import AsyncSession +T = TypeVar("T", bound="BaseCrud") + class BaseCrud: def __init__(self, session: AsyncSession): diff --git a/platform/reworkd_platform/db/crud/oauth.py b/platform/reworkd_platform/db/crud/oauth.py new file mode 100644 index 0000000000..032a051385 --- /dev/null +++ b/platform/reworkd_platform/db/crud/oauth.py @@ -0,0 +1,37 @@ +import secrets +from typing import Optional + +from fastapi import Depends +from sqlalchemy import select +from sqlalchemy.ext.asyncio import AsyncSession + +from reworkd_platform.db.crud.base import BaseCrud +from reworkd_platform.db.dependencies import get_db_session +from reworkd_platform.db.models.auth import OauthInstallation +from reworkd_platform.schemas import UserBase + + +class OAuthCrud(BaseCrud): + @classmethod + async def inject( + cls, + session: AsyncSession = Depends(get_db_session), + ) -> "OAuthCrud": + return cls(session) + + async def create_installation( + self, user: UserBase, provider: str + ) -> OauthInstallation: + return await OauthInstallation( + user_id=user.id, + organization_id=user.organization_id, + provider=provider, + state=secrets.token_hex(16), + ).save(self.session) + + async def get_installation_by_state( + self, state: str + ) -> Optional[OauthInstallation]: + query = select(OauthInstallation).filter(OauthInstallation.state == state) + + return (await self.session.execute(query)).scalar_one_or_none() diff --git a/platform/reworkd_platform/db/models/auth.py b/platform/reworkd_platform/db/models/auth.py index 5169344f1b..401ec75d88 100644 --- a/platform/reworkd_platform/db/models/auth.py +++ b/platform/reworkd_platform/db/models/auth.py @@ -1,4 +1,4 @@ -from sqlalchemy import String +from sqlalchemy import String, JSON from sqlalchemy.orm import mapped_column from reworkd_platform.db.base import TrackedModel @@ -17,3 +17,23 @@ class OrganizationUser(TrackedModel): user_id = mapped_column(String, nullable=False) organization_id = mapped_column(String, nullable=False) role = mapped_column(String, nullable=False, default="member") + + +class OauthCredentials(TrackedModel): + __tablename__ = "oauth_credentials" + + installation_id = mapped_column(String, nullable=False) + provider = mapped_column(String, nullable=False) + token_type = mapped_column(String, nullable=False) + access_token = mapped_column(String, nullable=False) + scope = mapped_column(String, nullable=True) + data = mapped_column(JSON, nullable=False) + + +class OauthInstallation(TrackedModel): + __tablename__ = "oauth_installation" + + user_id = mapped_column(String, nullable=False) + organization_id = mapped_column(String, nullable=True) + provider = mapped_column(String, nullable=False) + state = mapped_column(String, nullable=False) diff --git a/platform/reworkd_platform/services/oauth_installers.py b/platform/reworkd_platform/services/oauth_installers.py new file mode 100644 index 0000000000..854e0a1fb0 --- /dev/null +++ b/platform/reworkd_platform/services/oauth_installers.py @@ -0,0 +1,81 @@ +# from slack.web import WebClient +from abc import ABC, abstractmethod +from typing import TypeVar + +from fastapi import Depends, Path +from slack_sdk import WebClient +from slack_sdk.oauth import AuthorizeUrlGenerator + +from reworkd_platform.db.crud.oauth import OAuthCrud +from reworkd_platform.db.models.auth import OauthCredentials +from reworkd_platform.schemas import UserBase +from reworkd_platform.settings import Settings, settings as platform_settings +from reworkd_platform.web.api.http_responses import forbidden + +T = TypeVar("T", bound="OAuthInstaller") + + +class OAuthInstaller(ABC): + def __init__(self, crud: OAuthCrud, settings: Settings): + self.crud = crud + self.settings = settings + + @abstractmethod + async def install(self, user: UserBase) -> str: + raise NotImplementedError() + + @abstractmethod + async def install_callback(self, code: str, state: str) -> None: + raise NotImplementedError() + + +class SlackInstaller(OAuthInstaller): + PROVIDER = "slack" + + async def install(self, user: UserBase) -> str: + installation = await self.crud.create_installation(user, self.PROVIDER) + + return AuthorizeUrlGenerator( + client_id=self.settings.slack_client_id, + redirect_uri=self.settings.slack_redirect_uri, + scopes=["chat:write"], + ).generate( + state=installation.state, + ) + + async def install_callback(self, code: str, state: str) -> None: + installation = await self.crud.get_installation_by_state(state) + if not installation: + raise forbidden() + + oauth_response = WebClient().oauth_v2_access( + client_id=self.settings.slack_client_id, + client_secret=self.settings.slack_client_secret, + code=code, + state=state, + ) + + # We should handle token rotation / refresh tokens eventually + # TODO: encode token + await OauthCredentials( + installation_id=installation.id, + provider="slack", + token_type=oauth_response["token_type"], + access_token=oauth_response["access_token"], + scope=oauth_response["scope"], + data=oauth_response.data, + ).save(self.crud.session) + + +integrations = { + SlackInstaller.PROVIDER: SlackInstaller, +} + + +def installer_factory( + provider: str = Path(description="OAuth Provider"), + crud: OAuthCrud = Depends(OAuthCrud.inject), +) -> OAuthInstaller: + if provider in integrations: + return integrations[provider](crud, platform_settings) + raise NotImplementedError() diff --git a/platform/reworkd_platform/settings.py b/platform/reworkd_platform/settings.py index 5cf6726523..1acd7408c8 100644 --- a/platform/reworkd_platform/settings.py +++ b/platform/reworkd_platform/settings.py @@ -102,6 +102,11 @@ class Settings(BaseSettings): ff_mock_mode_enabled: bool = False # Controls whether calls are mocked max_loops: int = 25 # Maximum number of loops to run + # Settings for slack + slack_client_id: str = "" + slack_client_secret: str = "" + slack_redirect_uri: str = "" + @property def kafka_consumer_group(self) -> str: """ diff --git a/platform/reworkd_platform/tests/test_oauth_installers.py b/platform/reworkd_platform/tests/test_oauth_installers.py new file mode 100644 index 0000000000..a652e23182 --- /dev/null +++ b/platform/reworkd_platform/tests/test_oauth_installers.py @@ -0,0 +1,15 @@ +import pytest + +from reworkd_platform.services.oauth_installers import installer_factory + + +def test_installer_factory(mocker): + crud = mocker.Mock() + installer_factory("slack", crud) + + +def test_integration_dne(mocker): + crud = mocker.Mock() + + with pytest.raises(NotImplementedError): + installer_factory("asim", crud) diff --git a/platform/reworkd_platform/tests/workflow/test_if_condition.py b/platform/reworkd_platform/tests/workflow/test_if_condition.py index f225700a6e..9ca4884a0c 100644 --- a/platform/reworkd_platform/tests/workflow/test_if_condition.py +++ b/platform/reworkd_platform/tests/workflow/test_if_condition.py @@ -42,10 +42,11 @@ ], ) async def test_if_condition_success(value_one, operator, value_two, expected_result): + workflow_id = "123" block = IfCondition( input=IfInput(value_one=value_one, operator=operator, value_two=value_two) ) - result = await block.run(curr.workflow_id) + result = await block.run(workflow_id) assert result == IfOutput(result=expected_result) @@ -59,8 +60,9 @@ async def test_if_condition_success(value_one, operator, value_two, expected_res ], ) async def test_if_condition_errors(value_one, operator, value_two): + workflow_id = "123" block = IfCondition( input=IfInput(value_one=value_one, operator=operator, value_two=value_two) ) with pytest.raises(ValueError): - await block.run(curr.workflow_id) + await block.run(workflow_id) diff --git a/platform/reworkd_platform/web/api/auth/views.py b/platform/reworkd_platform/web/api/auth/views.py index 9c28d4b1f5..766d5c3ee1 100644 --- a/platform/reworkd_platform/web/api/auth/views.py +++ b/platform/reworkd_platform/web/api/auth/views.py @@ -4,6 +4,10 @@ from reworkd_platform.db.crud.organization import OrganizationCrud, OrganizationUsers from reworkd_platform.schemas import UserBase +from reworkd_platform.services.oauth_installers import ( + installer_factory, + OAuthInstaller, +) from reworkd_platform.services.sockets import websockets from reworkd_platform.web.api.dependencies import get_current_user @@ -44,3 +48,24 @@ async def pusher_authentication( user: UserBase = Depends(get_current_user), ) -> Dict[str, str]: return websockets.authenticate(user, channel_name, socket_id) + + +@router.get("/{provider}") +async def oauth_install( + user: UserBase = Depends(get_current_user), + installer: OAuthInstaller = Depends(installer_factory), +) -> str: + """Install an OAuth App""" + url = await installer.install(user) + print(url) + return url + + +@router.get("/{provider}/callback") +async def oauth_callback( + code: str, + state: str, + installer: OAuthInstaller = Depends(installer_factory), +) -> None: + """Callback for OAuth App""" + return await installer.install_callback(code, state) diff --git a/scripts/prepare-sync.sh b/scripts/prepare-sync.sh index c02d213e31..43aea17695 100755 --- a/scripts/prepare-sync.sh +++ b/scripts/prepare-sync.sh @@ -1,4 +1,6 @@ cd "$(dirname "$0")" || exit 1 +git reset --hard + git fetch origin git checkout main