diff --git a/.circleci/config.yml b/.circleci/config.yml
index 8fd9177a..5bcca6e9 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -5,6 +5,7 @@ jobs:
test:
docker:
- image: rishabhpoddar/supertokens_postgresql_plugin_test
+ - image: rishabhpoddar/oauth-server-cicd
resource_class: large
steps:
- add_ssh_keys:
diff --git a/.circleci/doOneMillionUsersTests.sh b/.circleci/doOneMillionUsersTests.sh
index ec82508d..2b4ca15d 100755
--- a/.circleci/doOneMillionUsersTests.sh
+++ b/.circleci/doOneMillionUsersTests.sh
@@ -93,6 +93,7 @@ do
cd ../../
git clone git@github.com:supertokens/supertokens-root.git
cd supertokens-root
+ rm gradle.properties
update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2
update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2
diff --git a/.circleci/doTests.sh b/.circleci/doTests.sh
index 554e23ae..6aae469b 100755
--- a/.circleci/doTests.sh
+++ b/.circleci/doTests.sh
@@ -93,6 +93,7 @@ do
cd ../../
git clone git@github.com:supertokens/supertokens-root.git
cd supertokens-root
+ rm gradle.properties
update-alternatives --install "/usr/bin/java" "java" "/usr/java/jdk-15.0.1/bin/java" 2
update-alternatives --install "/usr/bin/javac" "javac" "/usr/java/jdk-15.0.1/bin/javac" 2
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 4de1c9d4..2b22ae85 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,27 +1,38 @@
## Summary of change
+
(A few sentences about this PR)
## Related issues
+
- Link to issue1 here
- Link to issue1 here
## Test Plan
-(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your changes work. Bonus points for screenshots and videos!)
+
+(Write your test plan here. If you changed any code, please provide us with clear instructions on how you verified your
+changes work. Bonus points for screenshots and videos!)
## Documentation changes
-(If relevant, please create a PR in our [docs repo](https://github.com/supertokens/docs), or create a checklist here highlighting the necessary changes)
+
+(If relevant, please create a PR in our [docs repo](https://github.com/supertokens/docs), or create a checklist here
+highlighting the necessary changes)
## Checklist for important updates
+
- [ ] Changelog has been updated
- [ ] `pluginInterfaceSupported.json` file has been updated (if needed)
- [ ] Changes to the version if needed
- - In `build.gradle`
+ - In `build.gradle`
- [ ] Had installed and ran the pre-commit hook
-- [ ] If there are new dependencies that have been added in `build.gradle`, please make sure to add them in `implementationDependencies.json`.
+- [ ] If there are new dependencies that have been added in `build.gradle`, please make sure to add them
+ in `implementationDependencies.json`.
- [ ] Issue this PR against the latest non released version branch.
- - To know which one it is, run find the latest released tag (`git tag`) in the format `vX.Y.Z`, and then find the latest branch (`git branch --all`) whose `X.Y` is greater than the latest released tag.
- - If no such branch exists, then create one from the latest released branch.
+ - To know which one it is, run find the latest released tag (`git tag`) in the format `vX.Y.Z`, and then find the
+ latest branch (`git branch --all`) whose `X.Y` is greater than the latest released tag.
+ - If no such branch exists, then create one from the latest released branch.
- [ ] When adding new recipes, ensure that its performance is being measured in the `OneMillionUsersTest`
+
## Remaining TODOs for this PR
+
- [ ] Item1
- [ ] Item2
\ No newline at end of file
diff --git a/.github/helpers/package.json b/.github/helpers/package.json
index 80ec546b..28051cdb 100644
--- a/.github/helpers/package.json
+++ b/.github/helpers/package.json
@@ -1,14 +1,14 @@
{
- "name": "helpers",
- "version": "1.0.0",
- "description": "",
- "main": "test-pass-check-pr.js",
- "scripts": {
- "test": "echo \"Error: no test specified\" && exit 1"
- },
- "author": "",
- "license": "ISC",
- "dependencies": {
- "github-workflow-helpers": "github:supertokens/github-workflow-helpers"
- }
+ "name": "helpers",
+ "version": "1.0.0",
+ "description": "",
+ "main": "test-pass-check-pr.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "",
+ "license": "ISC",
+ "dependencies": {
+ "github-workflow-helpers": "github:supertokens/github-workflow-helpers"
+ }
}
\ No newline at end of file
diff --git a/.github/workflows/github-actions-changelog.yml b/.github/workflows/github-actions-changelog.yml
index 0007ca9a..47aaf4a2 100644
--- a/.github/workflows/github-actions-changelog.yml
+++ b/.github/workflows/github-actions-changelog.yml
@@ -1,15 +1,15 @@
name: "Enforcing changelog in PRs Workflow"
on:
pull_request:
- types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled]
+ types: [ opened, synchronize, reopened, ready_for_review, labeled, unlabeled ]
jobs:
# Enforces the update of a changelog file on every pull request
changelog:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v2
- - uses: dangoslen/changelog-enforcer@v2
- with:
- changeLogPath: 'CHANGELOG.md'
- skipLabels: 'Skip-Changelog'
\ No newline at end of file
+ - uses: actions/checkout@v2
+ - uses: dangoslen/changelog-enforcer@v2
+ with:
+ changeLogPath: 'CHANGELOG.md'
+ skipLabels: 'Skip-Changelog'
\ No newline at end of file
diff --git a/.github/workflows/lint-pr-title.yml b/.github/workflows/lint-pr-title.yml
index 904efde8..96281a74 100644
--- a/.github/workflows/lint-pr-title.yml
+++ b/.github/workflows/lint-pr-title.yml
@@ -1,20 +1,20 @@
name: "Lint PR Title"
on:
- pull_request:
- types:
- - opened
- - reopened
- - edited
- - synchronize
+ pull_request:
+ types:
+ - opened
+ - reopened
+ - edited
+ - synchronize
jobs:
- pr-title:
- name: Lint PR title
- runs-on: ubuntu-latest
- steps:
- - uses: amannn/action-semantic-pull-request@v3
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- validateSingleCommit: true
\ No newline at end of file
+ pr-title:
+ name: Lint PR title
+ runs-on: ubuntu-latest
+ steps:
+ - uses: amannn/action-semantic-pull-request@v3
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ validateSingleCommit: true
\ No newline at end of file
diff --git a/.github/workflows/tests-pass-check-pr.yml b/.github/workflows/tests-pass-check-pr.yml
index 4f03705b..07467d9d 100644
--- a/.github/workflows/tests-pass-check-pr.yml
+++ b/.github/workflows/tests-pass-check-pr.yml
@@ -1,24 +1,24 @@
name: "Check if \"Run tests\" action succeeded"
on:
- pull_request:
- types:
- - opened
- - reopened
- - edited
- - synchronize
+ pull_request:
+ types:
+ - opened
+ - reopened
+ - edited
+ - synchronize
jobs:
- pr-run-test-action:
- name: Check if "Run tests" action succeeded
- timeout-minutes: 60
- concurrency:
- group: ${{ github.head_ref }}
- cancel-in-progress: true
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - name: node install
- run: cd ./.github/helpers && npm i
- - name: Calling github API
- run: cd ./.github/helpers && GITHUB_TOKEN=${{ github.token }} REPO=${{ github.repository }} RUN_ID=${{ github.run_id }} BRANCH=${{ github.head_ref }} JOB_ID=${{ github.job }} SOURCE_OWNER=${{ github.event.pull_request.head.repo.owner.login }} CURRENT_SHA=${{ github.event.pull_request.head.sha }} node node_modules/github-workflow-helpers/test-pass-check-pr.js
\ No newline at end of file
+ pr-run-test-action:
+ name: Check if "Run tests" action succeeded
+ timeout-minutes: 60
+ concurrency:
+ group: ${{ github.head_ref }}
+ cancel-in-progress: true
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - name: node install
+ run: cd ./.github/helpers && npm i
+ - name: Calling github API
+ run: cd ./.github/helpers && GITHUB_TOKEN=${{ github.token }} REPO=${{ github.repository }} RUN_ID=${{ github.run_id }} BRANCH=${{ github.head_ref }} JOB_ID=${{ github.job }} SOURCE_OWNER=${{ github.event.pull_request.head.repo.owner.login }} CURRENT_SHA=${{ github.event.pull_request.head.sha }} node node_modules/github-workflow-helpers/test-pass-check-pr.js
\ No newline at end of file
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 8bb11ba3..dc2133e9 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -4,19 +4,19 @@ on:
inputs:
coreRepoOwnerName:
description: 'supertokens-core repo owner name'
- default: supertokens
+ default: supertokens
required: true
coreRepoBranch:
description: 'supertokens-core repos branch name'
- default: master
+ default: master
required: true
pluginRepoOwnerName:
description: 'supertokens-plugin-interface repo owner name'
- default: supertokens
+ default: supertokens
required: true
pluginInterfaceBranch:
description: 'supertokens-plugin-interface repos branch name'
- default: master
+ default: master
required: true
jobs:
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index 14ff0eb7..889d0053 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -1,18 +1,18 @@
-
-
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 6ed36dd3..3338eab6 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,4 +1,4 @@
-
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 35eb1ddf..9db25eef 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -1,6 +1,6 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 247a91ec..188fd938 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,10 +7,101 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
-## [7.1.0] - 2024-04-25
-
+- Unrestricts the connection pool size for the Bulk Import
- Adds queries for Bulk Import
+## [7.2.0] - 2024-10-03
+
+- Compatible with plugin interface version 6.3
+- Adds support for OAuthStorage
+
+### Migration
+
+```sql
+CREATE TABLE IF NOT EXISTS oauth_clients (
+ app_id VARCHAR(64),
+ client_id VARCHAR(255) NOT NULL,
+ is_client_credentials_only BOOLEAN NOT NULL,
+ PRIMARY KEY (app_id, client_id),
+ FOREIGN KEY(app_id) REFERENCES apps(app_id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS oauth_sessions (
+ gid VARCHAR(255),
+ app_id VARCHAR(64) DEFAULT 'public',
+ client_id VARCHAR(255) NOT NULL,
+ session_handle VARCHAR(128),
+ external_refresh_token VARCHAR(255) UNIQUE,
+ internal_refresh_token VARCHAR(255) UNIQUE,
+ jti TEXT NOT NULL,
+ exp BIGINT NOT NULL,
+ PRIMARY KEY (gid),
+ FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
+);
+
+CREATE INDEX IF NOT EXISTS oauth_session_exp_index ON oauth_sessions(exp DESC);
+CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON oauth_sessions(app_id, external_refresh_token DESC);
+
+CREATE TABLE IF NOT EXISTS oauth_m2m_tokens (
+ app_id VARCHAR(64) DEFAULT 'public',
+ client_id VARCHAR(255) NOT NULL,
+ iat BIGINT NOT NULL,
+ exp BIGINT NOT NULL,
+ PRIMARY KEY (app_id, client_id, iat),
+ FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
+);
+
+CREATE INDEX IF NOT EXISTS oauth_m2m_token_iat_index ON oauth_m2m_tokens(iat DESC, app_id DESC);
+CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON oauth_m2m_tokens(exp DESC);
+
+CREATE TABLE IF NOT EXISTS oauth_logout_challenges (
+ app_id VARCHAR(64) DEFAULT 'public',
+ challenge VARCHAR(128) NOT NULL,
+ client_id VARCHAR(255) NOT NULL,
+ post_logout_redirect_uri VARCHAR(1024),
+ session_handle VARCHAR(128),
+ state VARCHAR(128),
+ time_created BIGINT NOT NULL,
+ PRIMARY KEY (app_id, challenge),
+ FOREIGN KEY(app_id, client_id) REFERENCES oauth_clients(app_id, client_id) ON DELETE CASCADE
+);
+
+CREATE INDEX IF NOT EXISTS oauth_logout_challenges_time_created_index ON oauth_logout_challenges(time_created DESC);
+```
+
+## [7.1.3] - 2024-09-04
+
+- Adds index on `last_active_time` for `user_last_active` table to improve the performance of MAU computation.
+
+### Migration
+
+```sql
+CREATE INDEX IF NOT EXISTS user_last_active_last_active_time_index ON user_last_active (last_active_time DESC, app_id DESC);
+```
+
+## [7.1.2] - 2024-09-02
+
+- Optimizes users count query
+
+## [7.1.1] - 2024-08-08
+
+- Fixes tests that check for `Internal Error` in 500 status responses
+
+## [7.1.0]
+
+- Compatible with plugin interface version 6.2
+- Adds implementation for a new method `getConfigFieldsInfo` to fetch the plugin config fields.
+- Adds `DashboardInfo` annotations to the config properties in `PostgreSQLConfig`
+- Adds `null` state for `firstFactors` by adding `is_first_factors_null` field in `tenant_configs` table. The value of
+ this column is only applicable when there are no entries in the `tenant_first_factors` table for the tenant.
+
+### Migration
+
+```sql
+ALTER TABLE tenant_configs ADD COLUMN IF NOT EXISTS is_first_factors_null BOOLEAN DEFAULT TRUE;
+ALTER TABLE tenant_configs ALTER COLUMN is_first_factors_null DROP DEFAULT;
+```
+
## [7.0.1] - 2024-04-17
- Fixes issues with partial failures during tenant creation
@@ -21,8 +112,8 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- Support for MFA recipe
- Adds `firstFactors` and `requiredSecondaryFactors` for tenant config.
- Adds a new `useStaticKey` param to `updateSessionInfo_Transaction`
- - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to
- change the signing key type of a session
+ - This enables smooth switching between `useDynamicAccessTokenSigningKey` settings by allowing refresh calls to
+ change the signing key type of a session
### Migration
@@ -53,7 +144,8 @@ ALTER TABLE user_roles DROP CONSTRAINT IF EXISTS user_roles_role_fkey;
- Fixes the issue where passwords were inadvertently logged in the logs.
- Adds tests to check connection pool behaviour.
-- Adds `postgresql_idle_connection_timeout` and `postgresql_minimum_idle_connections` configs to control active connections to the database.
+- Adds `postgresql_idle_connection_timeout` and `postgresql_minimum_idle_connections` configs to control active
+ connections to the database.
## [5.0.6] - 2023-12-05
@@ -93,15 +185,16 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_
### Changes
- Support for Account Linking
- - Adds columns `primary_or_recipe_user_id`, `is_linked_or_is_a_primary_user` and `primary_or_recipe_user_time_joined` to `all_auth_recipe_users` table
- - Adds columns `primary_or_recipe_user_id` and `is_linked_or_is_a_primary_user` to `app_id_to_user_id` table
- - Removes index `all_auth_recipe_users_pagination_index` and addes `all_auth_recipe_users_pagination_index1`,
- `all_auth_recipe_users_pagination_index2`, `all_auth_recipe_users_pagination_index3` and
- `all_auth_recipe_users_pagination_index4` indexes instead on `all_auth_recipe_users` table
- - Adds `all_auth_recipe_users_recipe_id_index` on `all_auth_recipe_users` table
- - Adds `all_auth_recipe_users_primary_user_id_index` on `all_auth_recipe_users` table
- - Adds `email` column to `emailpassword_pswd_reset_tokens` table
- - Changes `user_id` foreign key constraint on `emailpassword_pswd_reset_tokens` to `app_id_to_user_id` table
+ - Adds columns `primary_or_recipe_user_id`, `is_linked_or_is_a_primary_user`
+ and `primary_or_recipe_user_time_joined` to `all_auth_recipe_users` table
+ - Adds columns `primary_or_recipe_user_id` and `is_linked_or_is_a_primary_user` to `app_id_to_user_id` table
+ - Removes index `all_auth_recipe_users_pagination_index` and addes `all_auth_recipe_users_pagination_index1`,
+ `all_auth_recipe_users_pagination_index2`, `all_auth_recipe_users_pagination_index3` and
+ `all_auth_recipe_users_pagination_index4` indexes instead on `all_auth_recipe_users` table
+ - Adds `all_auth_recipe_users_recipe_id_index` on `all_auth_recipe_users` table
+ - Adds `all_auth_recipe_users_primary_user_id_index` on `all_auth_recipe_users` table
+ - Adds `email` column to `emailpassword_pswd_reset_tokens` table
+ - Changes `user_id` foreign key constraint on `emailpassword_pswd_reset_tokens` to `app_id_to_user_id` table
### Migration
@@ -182,21 +275,20 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_
- Fixes null pointer issue when user belongs to no tenant.
-
## [4.0.1] - 2023-07-11
- Fixes duplicate users in users search queries when user is associated to multiple tenants
-
## [4.0.0] - 2023-06-02
### Changes
- Support for multitenancy
- - New tables `apps` and `tenants` have been added.
- - Schema of tables have been changed, adding `app_id` and `tenant_id` columns in tables and constraints & indexes have been modified to include this columns.
- - New user tables have been added to map users to apps and tenants.
- - New tables for multitenancy have been added.
+ - New tables `apps` and `tenants` have been added.
+ - Schema of tables have been changed, adding `app_id` and `tenant_id` columns in tables and constraints & indexes
+ have been modified to include this columns.
+ - New user tables have been added to map users to apps and tenants.
+ - New tables for multitenancy have been added.
- Increased transaction retry count to 50 from 20.
### Migration
@@ -1074,19 +1166,19 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_
### Migration
- If using `access_token_signing_key_dynamic` false in the core:
- - ```sql
- ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(true);
- ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT;
+ - ```sql
+ ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(true);
+ ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT;
```
- - ```sql
+ - ```sql
INSERT INTO jwt_signing_keys(key_id, key_string, algorithm, created_at)
select CONCAT('s-', created_at_time) as key_id, value as key_string, 'RS256' as algorithm, created_at_time as created_at
from session_access_token_signing_keys;
```
- If using `access_token_signing_key_dynamic` true (or not set) in the core:
- - ```sql
- ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(false);
- ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT;
+ - ```sql
+ ALTER TABLE session_info ADD COLUMN use_static_key BOOLEAN NOT NULL DEFAULT(false);
+ ALTER TABLE session_info ALTER COLUMN use_static_key DROP DEFAULT;
```
## [2.4.0] - 2023-03-30
@@ -1094,14 +1186,17 @@ CREATE INDEX IF NOT EXISTS app_id_to_user_id_primary_user_id_index ON app_id_to_
- Support for Dashboard Search
## [2.3.0] - 2023-03-27
+
- Support for TOTP recipe
- Support for active users
### Database changes
+
- Add new tables for TOTP recipe:
- - `totp_users` that stores the users that have enabled TOTP
- - `totp_user_devices` that stores devices (each device has its own secret) for each user
- - `totp_used_codes` that stores used codes for each user. This is to implement rate limiting and prevent replay attacks.
+ - `totp_users` that stores the users that have enabled TOTP
+ - `totp_user_devices` that stores devices (each device has its own secret) for each user
+ - `totp_used_codes` that stores used codes for each user. This is to implement rate limiting and prevent replay
+ attacks.
- Add `user_last_active` table to store the last active time of a user.
## [2.2.0] - 2023-02-21
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 53732985..a222aea4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,51 +1,70 @@
-
# Contributing
-We're so excited you're interested in helping with SuperTokens! We are happy to help you get started, even if you don't have any previous open-source experience :blush:
+We're so excited you're interested in helping with SuperTokens! We are happy to help you get started, even if you don't
+have any previous open-source experience :blush:
## New to Open Source?
-1. Take a look at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
-2. Go thorugh the [SuperTokens Code of Conduct](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CODE_OF_CONDUCT.md)
+
+1. Take a look
+ at [How to Contribute to an Open Source Project on GitHub](https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github)
+2. Go thorugh
+ the [SuperTokens Code of Conduct](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CODE_OF_CONDUCT.md)
## Where to ask Questions?
-1. Check our [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) to see if someone has already answered your question.
-2. Join our community on [Discord](https://supertokens.io/discord) and feel free to ask us your questions
+1. Check our [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues) to see if someone has
+ already answered your question.
+2. Join our community on [Discord](https://supertokens.io/discord) and feel free to ask us your questions
## Development Setup
+
### Prerequisites
+
- OS: Linux or macOS
- IDE: Intellij (recommended) or equivalent IDE
- PostgreSQL
### Project Setup
-1. Setup the `supertokens-core` by following [this guide](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#development-setup). If you are not modifying the `supertokens-core` repo, then you do not need to fork that.
+
+1. Setup the `supertokens-core` by
+ following [this guide](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#development-setup).
+ If you are not modifying the `supertokens-core` repo, then you do not need to fork that.
2. Start PostgreSQL on port `5432`, listening to `locahost` or `0.0.0.0`.
3. Create a PostgreSQL user (if not already exists) with username `root` and password `root`
4. Create a database called `supertokens`.
5. Fork the `supertokens-pstgresql-plugin` repository
-6. Open `modules.txt` in the `supertokens-root` directory and change it so that it looks like (the last line has changed):
+6. Open `modules.txt` in the `supertokens-root` directory and change it so that it looks like (the last line has
+ changed):
```
// put module name like module name,branch name,github username(if contributing with a forked repository) and then call ./loadModules script
core,master
plugin-interface,master
postgresql-plugin,master,
```
-7. Run `./loadModules` in the `supertokens-root` directory. This will clone your forked `supertokens-postgresql-plugin` repo.
-8. Follow the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#modifying-code) guide from `supertokens-core` repo for modifying and testing.
+7. Run `./loadModules` in the `supertokens-root` directory. This will clone your forked `supertokens-postgresql-plugin`
+ repo.
+8. Follow
+ the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-core/blob/master/CONTRIBUTING.md#modifying-code)
+ guide from `supertokens-core` repo for modifying and testing.
## Pull Request
+
1. Before submitting a pull request make sure all tests have passed
-2. Reference the relevant issue or pull request and give a clear description of changes/features added when submitting a pull request
+2. Reference the relevant issue or pull request and give a clear description of changes/features added when submitting a
+ pull request
3. Make sure the PR title follows [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) specification
## SuperTokens Community
-SuperTokens is made possible by a passionate team and a strong community of developers. If you have any questions or would like to get more involved in the SuperTokens community you can check out:
- - [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues)
- - [Discord](https://supertokens.io/discord)
- - [Twitter](https://twitter.com/supertokensio)
- - or [email us](mailto:team@supertokens.io)
-
+
+SuperTokens is made possible by a passionate team and a strong community of developers. If you have any questions or
+would like to get more involved in the SuperTokens community you can check out:
+
+- [Github Issues](https://github.com/supertokens/supertokens-postgresql-plugin/issues)
+- [Discord](https://supertokens.io/discord)
+- [Twitter](https://twitter.com/supertokensio)
+- or [email us](mailto:team@supertokens.io)
+
Additional resources you might find useful:
- - [SuperTokens Docs](https://supertokens.io/docs/community/getting-started/installation)
- - [Blog Posts](https://supertokens.io/blog/)
+
+- [SuperTokens Docs](https://supertokens.io/docs/community/getting-started/installation)
+- [Blog Posts](https://supertokens.io/blog/)
diff --git a/LICENSE.md b/LICENSE.md
index e105852e..7a85c9c7 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,191 +1,192 @@
- Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.
-
- This software is licensed under the Apache License, Version 2.0 (the
- "License") as published by the Apache Software Foundation.
-
- You may not use this software except in compliance with the License. A copy
- of the License is available below the line.
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- License for the specific language governing permissions and limitations
- under the License.
+Copyright (c) 2020, VRAI Labs and/or its affiliates. All rights reserved.
+
+This software is licensed under the Apache License, Version 2.0 (the
+"License") as published by the Apache Software Foundation.
+
+You may not use this software except in compliance with the License. A copy
+of the License is available below the line.
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+License for the specific language governing permissions and limitations
+under the License.
-------------------------------------------------------------------------------
+
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
+TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+END OF TERMS AND CONDITIONS
diff --git a/README.md b/README.md
index cc88cb0b..d44d464f 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,32 @@
-
![SuperTokens banner](https://raw.githubusercontent.com/supertokens/supertokens-logo/master/images/Artboard%20%E2%80%93%2027%402x.png)
# PostgreSQL plugin for SuperTokens Community
+
## About
+
This plugin is responsible for interfacing between SuperTokens Community version and an instance of PostgreSQL.
Learn more at https://supertokens.io
## Documentation
+
To see documentation, please click [here](https://supertokens.io/docs/community/introduction).
## Contributing
-Please refer to the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CONTRIBUTING.md) file in this repo.
+
+Please refer to
+the [CONTRIBUTING.md](https://github.com/supertokens/supertokens-postgresql-plugin/blob/master/CONTRIBUTING.md) file in
+this repo.
## Contact us
-For any queries, or support requests, please email us at team@supertokens.io, or join our [Discord](supertokens.io/discord) server.
+
+For any queries, or support requests, please email us at team@supertokens.io, or join
+our [Discord](supertokens.io/discord) server.
# Authors
+
Created with :heart: by the folks at SuperTokens.io.
diff --git a/build.gradle b/build.gradle
index fdcd09f6..1b24a626 100644
--- a/build.gradle
+++ b/build.gradle
@@ -2,7 +2,7 @@ plugins {
id 'java-library'
}
-version = "7.1.0"
+version = "7.2.1"
repositories {
mavenCentral()
diff --git a/config.yaml b/config.yaml
index 38ade78f..f42b0851 100644
--- a/config.yaml
+++ b/config.yaml
@@ -22,7 +22,7 @@ postgresql_config_version: 0
# (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. The PostgreSQL user to use to query the database.
# If the relevant tables are not already created by you, this user should have the
-# ability to create new tables. To see the tables needed, visit: https://supertokens.io/docs/community/getting-started/database-setup/postgresql
+# ability to create new tables. To see the tables needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database-setup/postgresql
# postgresql_user:
# (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. Password for the PostgreSQL user. If you have not set a password
@@ -37,7 +37,7 @@ postgresql_config_version: 0
# postgresql_table_schema:
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "") string value. A prefix to add to all table names managed by
-# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined
+# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined.
# postgresql_table_names_prefix:
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "key_value") string value. Specify the name of the table that will
@@ -66,7 +66,7 @@ postgresql_config_version: 0
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "thirdparty_users") string value. Specify the name of the table
# that will store the thirdparty recipe users.
-# postgresql_thirdparty_users_table_name
+# postgresql_thirdparty_users_table_name:
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: 60000) long value. Timeout in milliseconds for the idle connections
# to be closed.
diff --git a/devConfig.yaml b/devConfig.yaml
index a25dba97..09eb3e55 100644
--- a/devConfig.yaml
+++ b/devConfig.yaml
@@ -23,7 +23,7 @@ postgresql_config_version: 0
# (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. The PostgreSQL user to use to query the database.
# If the relevant tables are not already created by you, this user should have the
-# ability to create new tables. To see the tables needed, visit: TODO
+# ability to create new tables. To see the tables needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database-setup/postgresql
postgresql_user: "root"
# (DIFFERENT_ACROSS_TENANTS | COMPULSORY) string value. Password for the PostgreSQL instance. If you do not have a
@@ -39,7 +39,7 @@ postgresql_password: "root"
# postgresql_table_schema:
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "") string value. A prefix to add to all table names managed by
-# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined
+# SuperTokens. An "_" will be added between this prefix and the actual table name if the prefix is defined.
# postgresql_table_names_prefix:
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "key_value") string value. Specify the name of the table that will
@@ -68,7 +68,7 @@ postgresql_password: "root"
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: "thirdparty_users") string value. Specify the name of the table
# that will store the thirdparty recipe users.
-# postgresql_thirdparty_users_table_name
+# postgresql_thirdparty_users_table_name:
# (DIFFERENT_ACROSS_TENANTS | OPTIONAL | Default: 60000) long value. Timeout in milliseconds for the idle connections
# to be closed.
diff --git a/jar/postgresql-plugin-7.0.1.jar b/jar/postgresql-plugin-7.0.1.jar
deleted file mode 100644
index 7437a722..00000000
Binary files a/jar/postgresql-plugin-7.0.1.jar and /dev/null differ
diff --git a/jar/postgresql-plugin-7.2.0.jar b/jar/postgresql-plugin-7.2.0.jar
new file mode 100644
index 00000000..d8e15676
Binary files /dev/null and b/jar/postgresql-plugin-7.2.0.jar differ
diff --git a/pluginInterfaceSupported.json b/pluginInterfaceSupported.json
index 476e2b85..25f82381 100644
--- a/pluginInterfaceSupported.json
+++ b/pluginInterfaceSupported.json
@@ -1,6 +1,6 @@
{
"_comment": "contains a list of plugin interfaces branch names that this core supports",
"versions": [
- "6.1"
+ "6.3"
]
}
\ No newline at end of file
diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java
index b25df932..e7fa3d48 100644
--- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java
+++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyConnection.java
@@ -16,21 +16,7 @@
package io.supertokens.storage.postgresql;
-import java.sql.Array;
-import java.sql.Blob;
-import java.sql.CallableStatement;
-import java.sql.Clob;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.NClob;
-import java.sql.PreparedStatement;
-import java.sql.SQLClientInfoException;
-import java.sql.SQLException;
-import java.sql.SQLWarning;
-import java.sql.SQLXML;
-import java.sql.Savepoint;
-import java.sql.Statement;
-import java.sql.Struct;
+import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;
@@ -53,17 +39,17 @@ public BulkImportProxyConnection(Connection con) {
@Override
public void close() throws SQLException {
- // We simply ignore when close is called BulkImportProxyConnection
+// this.con.close();
}
@Override
public void commit() throws SQLException {
- // We simply ignore when commit is called BulkImportProxyConnection
+// this.con.commit();
}
@Override
public void rollback() throws SQLException {
- // We simply ignore when rollback is called BulkImportProxyConnection
+// this.con.rollback();
}
public void closeForBulkImportProxyStorage() throws SQLException {
diff --git a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java
index f4e8dd8e..4e2493ce 100644
--- a/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java
+++ b/src/main/java/io/supertokens/storage/postgresql/BulkImportProxyStorage.java
@@ -16,21 +16,16 @@
package io.supertokens.storage.postgresql;
-import java.sql.Connection;
-import java.sql.SQLException;
-import java.util.List;
-import java.util.Set;
-
-import com.google.gson.JsonObject;
-
-import io.supertokens.pluginInterface.LOG_LEVEL;
import io.supertokens.pluginInterface.exceptions.DbInitException;
-import io.supertokens.pluginInterface.exceptions.InvalidConfigException;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.sqlStorage.TransactionConnection;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.List;
+
/**
* BulkImportProxyStorage is a class extending Start, serving as a Storage instance in the bulk import user cronjob.
@@ -48,7 +43,7 @@ public synchronized Connection getTransactionConnection() throws SQLException, S
if (this.connection == null) {
Connection con = ConnectionPool.getConnectionForProxyStorage(this);
this.connection = new BulkImportProxyConnection(con);
- connection.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
+ connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
connection.setAutoCommit(false);
}
return this.connection;
@@ -62,17 +57,9 @@ protected T startTransactionHelper(TransactionLogic logic, TransactionIso
@Override
public void commitTransaction(TransactionConnection con) throws StorageQueryException {
- // We do not want to commit the queries when using the BulkImportProxyStorage to be able to rollback everything
+ // We do not want to commit the queries when using the BulkImportProxyStorage to be able to rollback everything
// if any query fails while importing the user
- }
-
- @Override
- public void loadConfig(JsonObject configJson, Set logLevels, TenantIdentifier tenantIdentifier)
- throws InvalidConfigException {
- // We are overriding the loadConfig method to set the connection pool size
- // to 1 to avoid creating many connections for the bulk import cronjob
- configJson.addProperty("postgresql_connection_pool_size", 1);
- super.loadConfig(configJson, logLevels, tenantIdentifier);
+// super.commitTransaction(con);
}
@Override
@@ -95,7 +82,7 @@ public void initStorage(boolean shouldWait, List tenantIdentif
public void closeConnectionForBulkImportProxyStorage() throws StorageQueryException {
try {
if (this.connection != null) {
- this.connection.close();
+ this.connection.closeForBulkImportProxyStorage();
this.connection = null;
}
ConnectionPool.close(this);
@@ -123,4 +110,12 @@ public void rollbackTransactionForBulkImportProxyStorage() throws StorageQueryEx
throw new StorageQueryException(e);
}
}
+
+ public void doVacuumFull() throws StorageQueryException {
+ try {
+ this.connection.prepareStatement("VACUUM FULL").execute();
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
}
diff --git a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java
index 62aa8d2a..70af1f53 100644
--- a/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java
+++ b/src/main/java/io/supertokens/storage/postgresql/ConnectionPool.java
@@ -149,7 +149,8 @@ static boolean isAlreadyInitialised(Start start) {
return getInstance(start) != null && getInstance(start).hikariDataSource != null;
}
- static void initPool(Start start, boolean shouldWait, PostConnectCallback postConnectCallback) throws DbInitException {
+ static void initPool(Start start, boolean shouldWait, PostConnectCallback postConnectCallback)
+ throws DbInitException {
if (isAlreadyInitialised(start)) {
return;
}
diff --git a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java
index 4fed4abc..9b408b5e 100644
--- a/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java
+++ b/src/main/java/io/supertokens/storage/postgresql/QueryExecutorTemplate.java
@@ -26,14 +26,14 @@
public interface QueryExecutorTemplate {
static T execute(Start start, String QUERY, PreparedStatementValueSetter setter,
- ResultSetValueExtractor mapper) throws SQLException, StorageQueryException {
+ ResultSetValueExtractor mapper) throws SQLException, StorageQueryException {
try (Connection con = ConnectionPool.getConnection(start)) {
return execute(con, QUERY, setter, mapper);
}
}
static T execute(Connection con, String QUERY, PreparedStatementValueSetter setter,
- ResultSetValueExtractor mapper) throws SQLException, StorageQueryException {
+ ResultSetValueExtractor mapper) throws SQLException, StorageQueryException {
if (setter == null)
setter = PreparedStatementValueSetter.NO_OP_SETTER;
try (PreparedStatement pst = con.prepareStatement(QUERY)) {
diff --git a/src/main/java/io/supertokens/storage/postgresql/Start.java b/src/main/java/io/supertokens/storage/postgresql/Start.java
index 411e52b3..268f63b8 100644
--- a/src/main/java/io/supertokens/storage/postgresql/Start.java
+++ b/src/main/java/io/supertokens/storage/postgresql/Start.java
@@ -25,9 +25,10 @@
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage;
-import io.supertokens.pluginInterface.bulkimport.BulkImportStorage.BULK_IMPORT_USER_STATUS;
-import io.supertokens.pluginInterface.bulkimport.sqlStorage.BulkImportSQLStorage;
+import io.supertokens.pluginInterface.bulkimport.BulkImportStorage;
import io.supertokens.pluginInterface.bulkimport.BulkImportUser;
+import io.supertokens.pluginInterface.bulkimport.exceptions.BulkImportTransactionRolledBackException;
+import io.supertokens.pluginInterface.bulkimport.sqlStorage.BulkImportSQLStorage;
import io.supertokens.pluginInterface.dashboard.DashboardSearchTags;
import io.supertokens.pluginInterface.dashboard.DashboardSessionInfo;
import io.supertokens.pluginInterface.dashboard.DashboardUser;
@@ -51,12 +52,20 @@
import io.supertokens.pluginInterface.jwt.JWTSigningKeyInfo;
import io.supertokens.pluginInterface.jwt.exceptions.DuplicateKeyIdException;
import io.supertokens.pluginInterface.jwt.sqlstorage.JWTRecipeSQLStorage;
-import io.supertokens.pluginInterface.multitenancy.*;
+import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
+import io.supertokens.pluginInterface.multitenancy.MultitenancyStorage;
+import io.supertokens.pluginInterface.multitenancy.TenantConfig;
+import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateClientTypeException;
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateTenantException;
import io.supertokens.pluginInterface.multitenancy.exceptions.DuplicateThirdPartyIdException;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.pluginInterface.multitenancy.sqlStorage.MultitenancySQLStorage;
+import io.supertokens.pluginInterface.oauth.OAuthClient;
+import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge;
+import io.supertokens.pluginInterface.oauth.OAuthStorage;
+import io.supertokens.pluginInterface.oauth.exception.DuplicateOAuthLogoutChallengeException;
+import io.supertokens.pluginInterface.oauth.exception.OAuthClientNotFoundException;
import io.supertokens.pluginInterface.passwordless.PasswordlessCode;
import io.supertokens.pluginInterface.passwordless.PasswordlessDevice;
import io.supertokens.pluginInterface.passwordless.exception.*;
@@ -101,7 +110,10 @@
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLTransactionRollbackException;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
@@ -109,20 +121,21 @@ public class Start
implements SessionSQLStorage, EmailPasswordSQLStorage, EmailVerificationSQLStorage, ThirdPartySQLStorage,
JWTRecipeSQLStorage, PasswordlessSQLStorage, UserMetadataSQLStorage, UserRolesSQLStorage, UserIdMappingStorage,
UserIdMappingSQLStorage, MultitenancyStorage, MultitenancySQLStorage, DashboardSQLStorage, TOTPSQLStorage,
- ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage, BulkImportSQLStorage {
+ ActiveUsersStorage, ActiveUsersSQLStorage, AuthRecipeSQLStorage, OAuthStorage, BulkImportSQLStorage {
// these configs are protected from being modified / viewed by the dev using the SuperTokens
// SaaS. If the core is not running in SuperTokens SaaS, this array has no effect.
private static String[] PROTECTED_DB_CONFIG = new String[]{"postgresql_connection_pool_size",
"postgresql_connection_uri", "postgresql_host", "postgresql_port", "postgresql_user", "postgresql_password",
"postgresql_database_name", "postgresql_table_schema", "postgresql_idle_connection_timeout",
- "postgresql_minimum_idle_connections"};
+ "postgresql_minimum_idle_connections", "postgresql_connection_attributes", "postgresql_connection_scheme",
+ "postgresql_table_names_prefix"
+ };
private static final Object appenderLock = new Object();
public static boolean silent = false;
private ResourceDistributor resourceDistributor = new ResourceDistributor();
private String processId;
private HikariLoggingAppender appender;
- private static final String APP_ID_KEY_NAME = "app_id";
private static final String ACCESS_TOKEN_SIGNING_KEY_NAME = "access_token_signing_key";
private static final String REFRESH_TOKEN_KEY_NAME = "refresh_token_key";
public static boolean isTesting = false;
@@ -322,6 +335,13 @@ public T startTransaction(TransactionLogic logic, TransactionIsolationLev
if ((isPSQLRollbackException || isDeadlockException) && tries < NUM_TRIES) {
try {
+ if(this instanceof BulkImportProxyStorage){
+ throw new StorageTransactionLogicException(new BulkImportTransactionRolledBackException(e));
+ // if the current instance is of BulkImportProxyStorage, that means we are doing a bulk import
+ // which uses nested transactions. With MySQL this retry logic doesn't going to work, we have
+ // to retry the whole "big" transaction, not just the innermost, current one.
+ // @see BulkImportTransactionRolledBackException for more explanation.
+ }
Thread.sleep((long) (10 + (250 + Math.min(Math.pow(2, tries), 3000)) * Math.random()));
} catch (InterruptedException ignored) {
}
@@ -867,7 +887,8 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi
}
} else if (className.equals(TOTPStorage.class.getName())) {
try {
- TOTPDevice device = new TOTPDevice(userId, "testDevice", "secret", 0, 30, false, System.currentTimeMillis());
+ TOTPDevice device = new TOTPDevice(userId, "testDevice", "secret", 0, 30, false,
+ System.currentTimeMillis());
this.startTransaction(con -> {
try {
long now = System.currentTimeMillis();
@@ -887,6 +908,11 @@ public void addInfoToNonAuthRecipesBasedOnUserId(TenantIdentifier tenantIdentifi
}
} else if (className.equals(JWTRecipeStorage.class.getName())) {
/* Since JWT recipe tables do not store userId we do not add any data to them */
+
+ } else if (className.equals(BulkImportStorage.class.getName())){
+ //ignore
+ } else if (className.equals(OAuthStorage.class.getName())) {
+ /* Since OAuth recipe tables do not store userId we do not add any data to them */
} else if (className.equals(ActiveUsersStorage.class.getName())) {
try {
ActiveUsersQueries.updateUserLastActive(this, tenantIdentifier.toAppIdentifier(), userId);
@@ -910,7 +936,7 @@ public String[] getProtectedConfigsFromSuperTokensSaaSUsers() {
@Override
public AuthRecipeUserInfo signUp(TenantIdentifier tenantIdentifier, String id, String email, String passwordHash,
- long timeJoined)
+ long timeJoined)
throws StorageQueryException, DuplicateUserIdException, DuplicateEmailException,
TenantOrAppNotFoundException {
try {
@@ -1220,8 +1246,10 @@ public boolean isEmailVerified(AppIdentifier appIdentifier, String userId, Strin
}
@Override
- public void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, String supertokensUserId, String externalUserId) throws StorageQueryException {
- EmailVerificationQueries.updateIsEmailVerifiedToExternalUserId(this, appIdentifier, supertokensUserId, externalUserId);
+ public void updateIsEmailVerifiedToExternalUserId(AppIdentifier appIdentifier, String supertokensUserId,
+ String externalUserId) throws StorageQueryException {
+ EmailVerificationQueries.updateIsEmailVerifiedToExternalUserId(this, appIdentifier, supertokensUserId,
+ externalUserId);
}
@Override
@@ -1353,6 +1381,15 @@ public void updateLastActive(AppIdentifier appIdentifier, String userId) throws
}
}
+ @TestOnly
+ public void updateLastActive(AppIdentifier appIdentifier, String userId, long timestamp) throws StorageQueryException {
+ try {
+ ActiveUsersQueries.updateUserLastActive(this, appIdentifier, userId, timestamp);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
@Override
public int countUsersActiveSince(AppIdentifier appIdentifier, long time) throws StorageQueryException {
try {
@@ -1742,10 +1779,10 @@ public void createCode(TenantIdentifier tenantIdentifier, PasswordlessCode code)
@Override
public AuthRecipeUserInfo createUser(TenantIdentifier tenantIdentifier,
- String id,
- @javax.annotation.Nullable String email,
- @javax.annotation.Nullable
- String phoneNumber, long timeJoined)
+ String id,
+ @javax.annotation.Nullable String email,
+ @javax.annotation.Nullable
+ String phoneNumber, long timeJoined)
throws StorageQueryException,
DuplicateEmailException, DuplicatePhoneNumberException, DuplicateUserIdException,
TenantOrAppNotFoundException {
@@ -2266,7 +2303,8 @@ public boolean updateOrDeleteExternalUserIdInfo(AppIdentifier appIdentifier, Str
}
@Override
- public HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier, ArrayList userIds)
+ public HashMap getUserIdMappingForSuperTokensIds(AppIdentifier appIdentifier,
+ ArrayList userIds)
throws StorageQueryException {
try {
return UserIdMappingQueries.getUserIdMappingWithUserIds(this, appIdentifier, userIds);
@@ -2385,7 +2423,8 @@ public TenantConfig[] getAllTenants() throws StorageQueryException {
}
@Override
- public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con, String userId)
+ public boolean addUserIdToTenant_Transaction(TenantIdentifier tenantIdentifier, TransactionConnection con,
+ String userId)
throws TenantOrAppNotFoundException, UnknownUserIdException, StorageQueryException,
DuplicateEmailException, DuplicateThirdPartyUserException, DuplicatePhoneNumberException {
Connection sqlCon = (Connection) con.getConnection();
@@ -2684,7 +2723,8 @@ public void createDevice(AppIdentifier appIdentifier, TOTPDevice device)
}
@Override
- public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier, TOTPDevice device)
+ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentifier appIdentifier,
+ TOTPDevice device)
throws StorageQueryException, DeviceAlreadyExistsException, TenantOrAppNotFoundException {
Connection sqlCon = (Connection) con.getConnection();
try {
@@ -2697,9 +2737,9 @@ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentif
ServerErrorMessage errMsg = ((PSQLException) actualException).getServerErrorMessage();
if (isPrimaryKeyError(errMsg, Config.getConfig(this).getTotpUserDevicesTable())) {
- throw new DeviceAlreadyExistsException();
+ throw new DeviceAlreadyExistsException();
} else if (isForeignKeyConstraintError(errMsg, Config.getConfig(this).getTotpUsersTable(), "app_id")) {
- throw new TenantOrAppNotFoundException(appIdentifier);
+ throw new TenantOrAppNotFoundException(appIdentifier);
}
}
throw new StorageQueryException(e);
@@ -2707,7 +2747,8 @@ public TOTPDevice createDevice_Transaction(TransactionConnection con, AppIdentif
}
@Override
- public TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId, String deviceName) throws StorageQueryException {
+ public TOTPDevice getDeviceByName_Transaction(TransactionConnection con, AppIdentifier appIdentifier, String userId,
+ String deviceName) throws StorageQueryException {
Connection sqlCon = (Connection) con.getConnection();
try {
return TOTPQueries.getDeviceByName_Transaction(this, sqlCon, appIdentifier, userId, deviceName);
@@ -2777,7 +2818,7 @@ public void updateDeviceName(AppIdentifier appIdentifier, String userId, String
if (e instanceof PSQLException) {
ServerErrorMessage errMsg = ((PSQLException) e).getServerErrorMessage();
if (isPrimaryKeyError(errMsg, Config.getConfig(this).getTotpUserDevicesTable())) {
- throw new DeviceAlreadyExistsException();
+ throw new DeviceAlreadyExistsException();
}
}
throw new StorageQueryException(e);
@@ -2856,6 +2897,11 @@ public Set getValidFieldsInConfig() {
return PostgreSQLConfig.getValidFields();
}
+ @Override
+ public List getPluginConfigFieldsInfo() {
+ return PostgreSQLConfig.getConfigFieldsInfoForDashboard(this);
+ }
+
@Override
public void setLogLevels(Set logLevels) {
Config.setLogLevels(this, logLevels);
@@ -2980,7 +3026,8 @@ public void linkAccounts_Transaction(AppIdentifier appIdentifier, TransactionCon
}
@Override
- public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId, String recipeUserId)
+ public void unlinkAccounts_Transaction(AppIdentifier appIdentifier, TransactionConnection con, String primaryUserId,
+ String recipeUserId)
throws StorageQueryException {
try {
Connection sqlCon = (Connection) con.getConnection();
@@ -3013,7 +3060,8 @@ public boolean checkIfUsesAccountLinking(AppIdentifier appIdentifier) throws Sto
}
@Override
- public int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException {
+ public int countUsersThatHaveMoreThanOneLoginMethodAndActiveSince(AppIdentifier appIdentifier, long sinceTime)
+ throws StorageQueryException {
try {
return ActiveUsersQueries.countUsersActiveSinceAndHasMoreThanOneLoginMethod(this, appIdentifier, sinceTime);
} catch (SQLException e) {
@@ -3042,11 +3090,13 @@ public UserIdMapping getUserIdMapping_Transaction(TransactionConnection con, App
try {
Connection sqlCon = (Connection) con.getConnection();
if (isSuperTokensUserId) {
- return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId_Transaction(this, sqlCon, appIdentifier,
+ return UserIdMappingQueries.getuseraIdMappingWithSuperTokensUserId_Transaction(this, sqlCon,
+ appIdentifier,
userId);
}
- return UserIdMappingQueries.getUserIdMappingWithExternalUserId_Transaction(this, sqlCon, appIdentifier, userId);
+ return UserIdMappingQueries.getUserIdMappingWithExternalUserId_Transaction(this, sqlCon, appIdentifier,
+ userId);
} catch (SQLException e) {
throw new StorageQueryException(e);
}
@@ -3067,7 +3117,8 @@ public UserIdMapping[] getUserIdMapping_Transaction(TransactionConnection con, A
}
@Override
- public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier) throws StorageQueryException {
+ public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier appIdentifier)
+ throws StorageQueryException {
try {
return GeneralQueries.getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(this, appIdentifier);
} catch (SQLException e) {
@@ -3076,7 +3127,9 @@ public int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(AppIdentifier ap
}
@Override
- public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(AppIdentifier appIdentifier, long sinceTime) throws StorageQueryException {
+ public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(AppIdentifier appIdentifier,
+ long sinceTime)
+ throws StorageQueryException {
try {
return ActiveUsersQueries.countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(this,
appIdentifier, sinceTime);
@@ -3085,6 +3138,18 @@ public int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(A
}
}
+
+ @Override
+ public boolean deleteOAuthClient(AppIdentifier appIdentifier, String clientId) throws StorageQueryException {
+ try {
+ return OAuthQueries.deleteOAuthClient(this, clientId, appIdentifier);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+
+
@TestOnly
public int getDbActivityCount(String dbname) throws SQLException, StorageQueryException {
String QUERY = "SELECT COUNT(*) as c FROM pg_stat_activity WHERE datname = ?;";
@@ -3111,10 +3176,43 @@ public void addBulkImportUsers(AppIdentifier appIdentifier, List
if (isPrimaryKeyError(serverErrorMessage, Config.getConfig(this).getBulkImportUsersTable())) {
throw new io.supertokens.pluginInterface.bulkimport.exceptions.DuplicateUserIdException();
}
- if (isForeignKeyConstraintError(serverErrorMessage, Config.getConfig(this).getBulkImportUsersTable(), "app_id")) {
+ if (isForeignKeyConstraintError(serverErrorMessage, Config.getConfig(this).getBulkImportUsersTable(),
+ "app_id")) {
throw new TenantOrAppNotFoundException(appIdentifier);
}
}
+ }
+ }
+
+ @Override
+ public OAuthClient getOAuthClientById(AppIdentifier appIdentifier, String clientId)
+ throws StorageQueryException, OAuthClientNotFoundException {
+ try {
+ OAuthClient client = OAuthQueries.getOAuthClientById(this, clientId, appIdentifier);
+ if (client == null) {
+ throw new OAuthClientNotFoundException();
+ }
+ return client;
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public void addOrUpdateOauthClient(AppIdentifier appIdentifier, String clientId, String clientSecret, boolean isClientCredentialsOnly, boolean enableRefreshTokenRotation)
+ throws StorageQueryException, TenantOrAppNotFoundException {
+ try {
+ OAuthQueries.addOrUpdateOauthClient(this, appIdentifier, clientId, clientSecret, isClientCredentialsOnly, enableRefreshTokenRotation);
+ } catch (SQLException e) {
+ ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage();
+ PostgreSQLConfig config = Config.getConfig(this);
+
+ if (isForeignKeyConstraintError(
+ errorMessage,
+ config.getOAuthClientsTable(),
+ "app_id")) {
+ throw new TenantOrAppNotFoundException(appIdentifier);
+ }
throw new StorageQueryException(e);
}
}
@@ -3129,6 +3227,15 @@ public List getBulkImportUsers(AppIdentifier appIdentifier, @Non
}
}
+ @Override
+ public List getOAuthClients(AppIdentifier appIdentifier, List clientIds) throws StorageQueryException {
+ try {
+ return OAuthQueries.getOAuthClients(this, appIdentifier, clientIds);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
@Override
public void updateBulkImportUserStatus_Transaction(AppIdentifier appIdentifier, TransactionConnection con, @Nonnull String bulkImportUserId, @Nonnull BULK_IMPORT_USER_STATUS status, @Nullable String errorMessage)
throws StorageQueryException {
@@ -3140,6 +3247,25 @@ public void updateBulkImportUserStatus_Transaction(AppIdentifier appIdentifier,
}
}
+ @Override
+ public boolean revokeOAuthTokenByGID(AppIdentifier appIdentifier, String gid) throws StorageQueryException {
+ try {
+ return OAuthQueries.deleteOAuthSessionByGID(this, appIdentifier, gid);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public boolean revokeOAuthTokenByClientId(AppIdentifier appIdentifier, String clientId)
+ throws StorageQueryException {
+ try {
+ return OAuthQueries.deleteOAuthSessionByClientId(this, appIdentifier, clientId);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
@Override
public List deleteBulkImportUsers(AppIdentifier appIdentifier, @Nonnull String[] bulkImportUserIds) throws StorageQueryException {
try {
@@ -3149,6 +3275,16 @@ public List deleteBulkImportUsers(AppIdentifier appIdentifier, @Nonnull
}
}
+ @Override
+ public boolean revokeOAuthTokenByJTI(AppIdentifier appIdentifier, String gid, String jti)
+ throws StorageQueryException {
+ try {
+ return OAuthQueries.deleteJTIFromOAuthSession(this, appIdentifier, gid, jti);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
@Override
public List getBulkImportUsersAndChangeStatusToProcessing(AppIdentifier appIdentifier, @Nonnull Integer limit) throws StorageQueryException {
try {
@@ -3167,6 +3303,16 @@ public void updateBulkImportUserPrimaryUserId(AppIdentifier appIdentifier, @Nonn
}
}
+ @Override
+ public boolean revokeOAuthTokenBySessionHandle(AppIdentifier appIdentifier, String sessionHandle)
+ throws StorageQueryException {
+ try {
+ return OAuthQueries.deleteOAuthSessionBySessionHandle(this, appIdentifier, sessionHandle);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
@Override
public long getBulkImportUsersCount(AppIdentifier appIdentifier, @Nullable BULK_IMPORT_USER_STATUS status) throws StorageQueryException {
try {
@@ -3175,4 +3321,179 @@ public long getBulkImportUsersCount(AppIdentifier appIdentifier, @Nullable BULK_
throw new StorageQueryException(e);
}
}
+
+ @Override
+ public void addOAuthM2MTokenForStats(AppIdentifier appIdentifier, String clientId, long iat, long exp)
+ throws StorageQueryException, OAuthClientNotFoundException {
+ try {
+ OAuthQueries.addOAuthM2MTokenForStats(this, appIdentifier, clientId, iat, exp);
+ } catch (SQLException e) {
+ ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage();
+ PostgreSQLConfig config = Config.getConfig(this);
+
+ if (isForeignKeyConstraintError(
+ errorMessage,
+ config.getOAuthM2MTokensTable(),
+ "client_id")) {
+ throw new OAuthClientNotFoundException();
+ }
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public void deleteExpiredOAuthM2MTokens(long exp) throws StorageQueryException {
+ try {
+ OAuthQueries.deleteExpiredOAuthM2MTokens(this, exp);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public void addOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge, String clientId,
+ String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated)
+ throws StorageQueryException, DuplicateOAuthLogoutChallengeException, OAuthClientNotFoundException {
+ try {
+ OAuthQueries.addOAuthLogoutChallenge(this, appIdentifier, challenge, clientId, postLogoutRedirectionUri, sessionHandle, state, timeCreated);
+ } catch (SQLException e) {
+ ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage();
+ PostgreSQLConfig config = Config.getConfig(this);
+
+ if (isPrimaryKeyError(errorMessage, config.getOAuthLogoutChallengesTable())) {
+ throw new DuplicateOAuthLogoutChallengeException();
+ } else if (isForeignKeyConstraintError(
+ errorMessage,
+ config.getOAuthLogoutChallengesTable(),
+ "client_id")) {
+ throw new OAuthClientNotFoundException();
+ }
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public OAuthLogoutChallenge getOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException {
+ try {
+ return OAuthQueries.getOAuthLogoutChallenge(this, appIdentifier, challenge);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public void deleteOAuthLogoutChallenge(AppIdentifier appIdentifier, String challenge) throws StorageQueryException {
+ try {
+ OAuthQueries.deleteOAuthLogoutChallenge(this, appIdentifier, challenge);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public void deleteOAuthLogoutChallengesBefore(long time) throws StorageQueryException {
+ try {
+ OAuthQueries.deleteOAuthLogoutChallengesBefore(this, time);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public void createOrUpdateOAuthSession(AppIdentifier appIdentifier, String gid, String clientId,
+ String externalRefreshToken, String internalRefreshToken,
+ String sessionHandle, String jti, long exp)
+ throws StorageQueryException, OAuthClientNotFoundException {
+ try {
+ OAuthQueries.createOrUpdateOAuthSession(this, appIdentifier, gid, clientId, externalRefreshToken,
+ internalRefreshToken, sessionHandle, jti, exp);
+ } catch (SQLException e) {
+ ServerErrorMessage errorMessage = ((PSQLException) e).getServerErrorMessage();
+ PostgreSQLConfig config = Config.getConfig(this);
+
+ if (isForeignKeyConstraintError(
+ errorMessage,
+ config.getOAuthSessionsTable(),
+ "client_id")) {
+ throw new OAuthClientNotFoundException();
+ }
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public String getRefreshTokenMapping(AppIdentifier appIdentifier, String externalRefreshToken)
+ throws StorageQueryException {
+ try {
+ return OAuthQueries.getRefreshTokenMapping(this, appIdentifier, externalRefreshToken);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public void deleteExpiredOAuthSessions(long exp) throws StorageQueryException {
+ try {
+ OAuthQueries.deleteExpiredOAuthSessions(this, exp);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public int countTotalNumberOfOAuthClients(AppIdentifier appIdentifier) throws StorageQueryException {
+ try {
+ return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, false);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public int countTotalNumberOfClientCredentialsOnlyOAuthClients(AppIdentifier appIdentifier)
+ throws StorageQueryException {
+ try {
+ return OAuthQueries.countTotalNumberOfClients(this, appIdentifier, true);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public int countTotalNumberOfOAuthM2MTokensCreatedSince(AppIdentifier appIdentifier, long since)
+ throws StorageQueryException {
+ try {
+ return OAuthQueries.countTotalNumberOfOAuthM2MTokensCreatedSince(this, appIdentifier, since);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public int countTotalNumberOfOAuthM2MTokensAlive(AppIdentifier appIdentifier) throws StorageQueryException {
+ try {
+ return OAuthQueries.countTotalNumberOfOAuthM2MTokensAlive(this, appIdentifier);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public boolean isOAuthTokenRevokedByGID(AppIdentifier appIdentifier, String gid) throws StorageQueryException {
+ try {
+ return !OAuthQueries.isOAuthSessionExistsByGID(this, appIdentifier, gid);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
+
+ @Override
+ public boolean isOAuthTokenRevokedByJTI(AppIdentifier appIdentifier, String gid, String jti)
+ throws StorageQueryException {
+ try {
+ return !OAuthQueries.isOAuthSessionExistsByJTI(this, appIdentifier, gid, jti);
+ } catch (SQLException e) {
+ throw new StorageQueryException(e);
+ }
+ }
}
diff --git a/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java b/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java
new file mode 100644
index 00000000..9bc1ced3
--- /dev/null
+++ b/src/main/java/io/supertokens/storage/postgresql/annotations/DashboardInfo.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
+ *
+ * This software is licensed under the Apache License, Version 2.0 (the
+ * "License") as published by the Apache Software Foundation.
+ *
+ * You may not use this file except in compliance with the License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.supertokens.storage.postgresql.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to provide a description for a configuration fields. To be used on the fields of `CoreConfig` and config
+ * class in the plugin like `PostgreSQLConfig`, `MysqlConfig`, etc.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+// Make annotation accessible at runtime so that config descriptions can be read from API
+@Target(ElementType.FIELD) // Annotation can only be applied to fields
+public @interface DashboardInfo {
+ String description() default "";
+
+ boolean isOptional() default false;
+
+ String defaultValue() default "";
+
+ boolean isEditable() default false;
+}
diff --git a/src/main/java/io/supertokens/storage/postgresql/config/Config.java b/src/main/java/io/supertokens/storage/postgresql/config/Config.java
index 41088d73..1ce21e06 100644
--- a/src/main/java/io/supertokens/storage/postgresql/config/Config.java
+++ b/src/main/java/io/supertokens/storage/postgresql/config/Config.java
@@ -53,7 +53,8 @@ private static Config getInstance(Start start) {
return (Config) start.getResourceDistributor().getResource(RESOURCE_KEY);
}
- public static void loadConfig(Start start, JsonObject configJson, Set logLevels, TenantIdentifier tenantIdentifier)
+ public static void loadConfig(Start start, JsonObject configJson, Set logLevels,
+ TenantIdentifier tenantIdentifier)
throws InvalidConfigException {
if (getInstance(start) != null) {
return;
diff --git a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java
index bd206366..6e915ca8 100644
--- a/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java
+++ b/src/main/java/io/supertokens/storage/postgresql/config/PostgreSQLConfig.java
@@ -19,21 +19,18 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
+import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
+import io.supertokens.pluginInterface.ConfigFieldInfo;
import io.supertokens.pluginInterface.exceptions.InvalidConfigException;
-import io.supertokens.storage.postgresql.annotations.ConnectionPoolProperty;
-import io.supertokens.storage.postgresql.annotations.IgnoreForAnnotationCheck;
-import io.supertokens.storage.postgresql.annotations.NotConflictingWithinUserPool;
-import io.supertokens.storage.postgresql.annotations.UserPoolProperty;
+import io.supertokens.storage.postgresql.Start;
+import io.supertokens.storage.postgresql.annotations.*;
import java.lang.reflect.Field;
import java.net.URI;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
+import java.util.*;
@JsonIgnoreProperties(ignoreUnknown = true)
public class PostgreSQLConfig {
@@ -44,80 +41,140 @@ public class PostgreSQLConfig {
@JsonProperty
@ConnectionPoolProperty
+ @DashboardInfo(
+ description = "Defines the connection pool size to PostgreSQL. Please see https://github" +
+ ".com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing",
+ defaultValue = "10", isOptional = true, isEditable = true)
private int postgresql_connection_pool_size = 10;
@JsonProperty
@UserPoolProperty
+ @DashboardInfo(
+ description = "Specify the postgresql host url here. For example: - \"localhost\" - \"192.168.0.1\" - " +
+ "\"\" - \"example.com\"",
+ defaultValue = "\"localhost\"", isOptional = true)
private String postgresql_host = null;
@JsonProperty
@UserPoolProperty
+ @DashboardInfo(description = "Specify the port to use when connecting to PostgreSQL instance.",
+ defaultValue = "5432", isOptional = true)
private int postgresql_port = -1;
@JsonProperty
@ConnectionPoolProperty
+ @DashboardInfo(
+ description = "The PostgreSQL user to use to query the database. If the relevant tables are not already " +
+ "created by you, this user should have the ability to create new tables. To see the tables " +
+ "needed, visit: https://supertokens.com/docs/thirdpartyemailpassword/pre-built-ui/setup/database" +
+ "-setup/postgresql",
+ defaultValue = "null")
private String postgresql_user = null;
@JsonProperty
@ConnectionPoolProperty
+ @DashboardInfo(
+ description = "Password for the PostgreSQL user. If you have not set a password make this an empty string.",
+ defaultValue = "null")
private String postgresql_password = null;
@JsonProperty
@UserPoolProperty
+ @DashboardInfo(description = "The database name to store SuperTokens related data.",
+ defaultValue = "\"supertokens\"", isOptional = true)
private String postgresql_database_name = null;
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(
+ description = "A prefix to add to all table names managed by SuperTokens. An \"_\" will be added between " +
+ "this prefix and the actual table name if the prefix is defined.",
+ defaultValue = "\"\"", isOptional = true)
private String postgresql_table_names_prefix = "";
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(
+ description = "Specify the name of the table that will store secret keys and app info necessary for the " +
+ "functioning sessions.",
+ defaultValue = "\"key_value\"", isOptional = true)
private String postgresql_key_value_table_name = null;
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(description = "Specify the name of the table that will store the session info for users.",
+ defaultValue = "\"session_info\"", isOptional = true)
private String postgresql_session_info_table_name = null;
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(
+ description = "Specify the name of the table that will store the user information, along with their email" +
+ " and hashed password.",
+ defaultValue = "\"emailpassword_users\"", isOptional = true)
private String postgresql_emailpassword_users_table_name = null;
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(description = "Specify the name of the table that will store the password reset tokens for users.",
+ defaultValue = "\"emailpassword_pswd_reset_tokens\"", isOptional = true)
private String postgresql_emailpassword_pswd_reset_tokens_table_name = null;
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(
+ description = "Specify the name of the table that will store the email verification tokens for users.",
+ defaultValue = "\"emailverification_tokens\"", isOptional = true)
private String postgresql_emailverification_tokens_table_name = null;
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(description = "Specify the name of the table that will store the verified email addresses.",
+ defaultValue = "\"emailverification_verified_emails\"", isOptional = true)
private String postgresql_emailverification_verified_emails_table_name = null;
@JsonProperty
@NotConflictingWithinUserPool
+ @DashboardInfo(description = "Specify the name of the table that will store the thirdparty recipe users.",
+ defaultValue = "\"thirdparty_users\"", isOptional = true)
private String postgresql_thirdparty_users_table_name = null;
@JsonProperty
@UserPoolProperty
+ @DashboardInfo(description = "The schema for tables.", defaultValue = "\"public\"", isOptional = true)
private String postgresql_table_schema = "public";
@JsonProperty
@IgnoreForAnnotationCheck
+ @DashboardInfo(
+ description = "Specify the PostgreSQL connection URI in the following format: " +
+ "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2... Values provided " +
+ "via other configs will override values provided by this config.",
+ defaultValue = "null", isOptional = true)
private String postgresql_connection_uri = null;
@ConnectionPoolProperty
+ @DashboardInfo(description = "The connection attributes of the PostgreSQL database.",
+ defaultValue = "\"allowPublicKeyRetrieval=true\"", isOptional = true)
private String postgresql_connection_attributes = "allowPublicKeyRetrieval=true";
@ConnectionPoolProperty
+ @DashboardInfo(description = "The scheme of the PostgreSQL database.", defaultValue = "\"postgresql\"",
+ isOptional = true)
private String postgresql_connection_scheme = "postgresql";
@JsonProperty
@ConnectionPoolProperty
+ @DashboardInfo(description = "Timeout in milliseconds for the idle connections to be closed.",
+ defaultValue = "60000", isOptional = true, isEditable = true)
private long postgresql_idle_connection_timeout = 60000;
@JsonProperty
@ConnectionPoolProperty
+ @DashboardInfo(
+ description = "Minimum number of idle connections to be kept active. If not set, minimum idle connections" +
+ " will be same as the connection pool size.",
+ defaultValue = "null", isOptional = true, isEditable = true)
private Integer postgresql_minimum_idle_connections = null;
@IgnoreForAnnotationCheck
@@ -134,6 +191,69 @@ public static Set getValidFields() {
return validFields;
}
+ public static ArrayList getConfigFieldsInfoForDashboard(Start start) {
+ ArrayList result = new ArrayList();
+
+ JsonObject tenantConfig = new Gson().toJsonTree(Config.getConfig(start)).getAsJsonObject();
+
+ PostgreSQLConfig defaultConfigObj = new PostgreSQLConfig();
+ try {
+ defaultConfigObj.validateAndNormalise(true); // skip validation and just populate defaults
+ } catch (InvalidConfigException e) {
+ throw new IllegalStateException(e); // should never happen
+ }
+
+ JsonObject defaultConfig = new Gson().toJsonTree(defaultConfigObj).getAsJsonObject();
+
+ for (String fieldId : PostgreSQLConfig.getValidFields()) {
+ try {
+ Field field = PostgreSQLConfig.class.getDeclaredField(fieldId);
+ if (!field.isAnnotationPresent(DashboardInfo.class)) {
+ continue;
+ }
+
+ if (field.getName().endsWith("_table_name")) {
+ continue; // do not show
+ }
+
+ String key = field.getName();
+ String description = field.isAnnotationPresent(DashboardInfo.class)
+ ? field.getAnnotation(DashboardInfo.class).description()
+ : "";
+ boolean isDifferentAcrossTenants = true;
+
+ String valueType = null;
+
+ Class> fieldType = field.getType();
+
+ if (fieldType == String.class) {
+ valueType = "string";
+ } else if (fieldType == boolean.class) {
+ valueType = "boolean";
+ } else if (fieldType == int.class || fieldType == long.class || fieldType == Integer.class) {
+ valueType = "number";
+ } else {
+ throw new RuntimeException("Unknown field type " + fieldType.getName());
+ }
+
+ JsonElement value = tenantConfig.get(field.getName());
+
+ JsonElement defaultValue = defaultConfig.get(field.getName());
+ boolean isNullable = defaultValue == null;
+
+ boolean isEditable = field.getAnnotation(DashboardInfo.class).isEditable();
+
+ result.add(new ConfigFieldInfo(
+ key, valueType, value, description, isDifferentAcrossTenants,
+ null, isNullable, defaultValue, true, isEditable));
+
+ } catch (NoSuchFieldException e) {
+ continue;
+ }
+ }
+ return result;
+ }
+
public String getTableSchema() {
return postgresql_table_schema;
}
@@ -314,6 +434,7 @@ public String getDashboardSessionsTable() {
return addSchemaAndPrefixToTableName("dashboard_user_sessions");
}
+
public String getTotpUsersTable() {
return addSchemaAndPrefixToTableName("totp_users");
}
@@ -325,6 +446,21 @@ public String getTotpUserDevicesTable() {
public String getTotpUsedCodesTable() {
return addSchemaAndPrefixToTableName("totp_used_codes");
}
+ public String getOAuthClientsTable() {
+ return addSchemaAndPrefixToTableName("oauth_clients");
+ }
+
+ public String getOAuthM2MTokensTable() {
+ return addSchemaAndPrefixToTableName("oauth_m2m_tokens");
+ }
+
+ public String getOAuthSessionsTable() {
+ return addSchemaAndPrefixToTableName("oauth_sessions");
+ }
+
+ public String getOAuthLogoutChallengesTable() {
+ return addSchemaAndPrefixToTableName("oauth_logout_challenges");
+ }
public String getBulkImportUsersTable() {
return addSchemaAndPrefixToTableName("bulk_import_users");
@@ -343,41 +479,48 @@ private String addSchemaToTableName(String tableName) {
}
public void validateAndNormalise() throws InvalidConfigException {
+ validateAndNormalise(false);
+ }
+
+ private void validateAndNormalise(boolean skipValidation) throws InvalidConfigException {
if (isValidAndNormalised) {
return;
}
- if (postgresql_connection_uri != null) {
- try {
- URI ignored = URI.create(postgresql_connection_uri);
- } catch (Exception e) {
- throw new InvalidConfigException(
- "The provided postgresql connection URI has an incorrect format. Please use a format like "
- + "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2...");
- }
- } else {
- if (this.getUser() == null) {
- throw new InvalidConfigException(
- "'postgresql_user' and 'postgresql_connection_uri' are not set. Please set at least one of "
- + "these values");
+ if (!skipValidation) {
+ if (postgresql_connection_uri != null) {
+ try {
+ URI ignored = URI.create(postgresql_connection_uri);
+ } catch (Exception e) {
+ throw new InvalidConfigException(
+ "The provided postgresql connection URI has an incorrect format. Please use a format like "
+ +
+ "postgresql://[user[:[password]]@]host[:port][/dbname][?attr1=val1&attr2=val2...");
+ }
+ } else {
+ if (this.getUser() == null) {
+ throw new InvalidConfigException(
+ "'postgresql_user' and 'postgresql_connection_uri' are not set. Please set at least one of "
+ + "these values");
+ }
}
- }
- if (postgresql_connection_pool_size <= 0) {
- throw new InvalidConfigException(
- "'postgresql_connection_pool_size' in the config.yaml file must be > 0");
- }
-
- if (postgresql_minimum_idle_connections != null) {
- if (postgresql_minimum_idle_connections < 0) {
+ if (postgresql_connection_pool_size <= 0) {
throw new InvalidConfigException(
- "'postgresql_minimum_idle_connections' must be >= 0");
+ "'postgresql_connection_pool_size' in the config.yaml file must be > 0");
}
- if (postgresql_minimum_idle_connections > postgresql_connection_pool_size) {
- throw new InvalidConfigException(
- "'postgresql_minimum_idle_connections' must be less than or equal to "
- + "'postgresql_connection_pool_size'");
+ if (postgresql_minimum_idle_connections != null) {
+ if (postgresql_minimum_idle_connections < 0) {
+ throw new InvalidConfigException(
+ "'postgresql_minimum_idle_connections' must be >= 0");
+ }
+
+ if (postgresql_minimum_idle_connections > postgresql_connection_pool_size) {
+ throw new InvalidConfigException(
+ "'postgresql_minimum_idle_connections' must be less than or equal to "
+ + "'postgresql_connection_pool_size'");
+ }
}
}
@@ -526,21 +669,26 @@ public void validateAndNormalise() throws InvalidConfigException {
}
if (postgresql_emailpassword_pswd_reset_tokens_table_name != null) {
- postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaToTableName(postgresql_emailpassword_pswd_reset_tokens_table_name);
+ postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaToTableName(
+ postgresql_emailpassword_pswd_reset_tokens_table_name);
} else {
- postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaAndPrefixToTableName("emailpassword_pswd_reset_tokens");
+ postgresql_emailpassword_pswd_reset_tokens_table_name = addSchemaAndPrefixToTableName(
+ "emailpassword_pswd_reset_tokens");
}
if (postgresql_emailverification_tokens_table_name != null) {
- postgresql_emailverification_tokens_table_name = addSchemaToTableName(postgresql_emailverification_tokens_table_name);
+ postgresql_emailverification_tokens_table_name = addSchemaToTableName(
+ postgresql_emailverification_tokens_table_name);
} else {
postgresql_emailverification_tokens_table_name = addSchemaAndPrefixToTableName("emailverification_tokens");
}
if (postgresql_emailverification_verified_emails_table_name != null) {
- postgresql_emailverification_verified_emails_table_name = addSchemaToTableName(postgresql_emailverification_verified_emails_table_name);
+ postgresql_emailverification_verified_emails_table_name = addSchemaToTableName(
+ postgresql_emailverification_verified_emails_table_name);
} else {
- postgresql_emailverification_verified_emails_table_name = addSchemaAndPrefixToTableName("emailverification_verified_emails");
+ postgresql_emailverification_verified_emails_table_name = addSchemaAndPrefixToTableName(
+ "emailverification_verified_emails");
}
if (postgresql_thirdparty_users_table_name != null) {
@@ -592,10 +740,11 @@ public String getConnectionPoolId() {
try {
String fieldName = field.getName();
String fieldValue = field.get(this) != null ? field.get(this).toString() : null;
- if(fieldValue == null) {
+ if (fieldValue == null) {
continue;
}
- // To ensure a unique connectionPoolId we include the database password and use the "|db_pass|" identifier.
+ // To ensure a unique connectionPoolId we include the database password and use the "|db_pass|"
+ // identifier.
// This facilitates easy removal of the password from logs when necessary.
if (fieldName.equals("postgresql_password")) {
connectionPoolId.append("|db_pass|" + fieldValue + "|db_pass");
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java
index 3a39c384..ccd589ac 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/ActiveUsersQueries.java
@@ -9,6 +9,8 @@
import java.sql.Connection;
import java.sql.SQLException;
+import org.jetbrains.annotations.TestOnly;
+
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update;
import static io.supertokens.storage.postgresql.config.Config.getConfig;
@@ -34,6 +36,11 @@ static String getQueryToCreateAppIdIndexForUserLastActiveTable(Start start) {
+ Config.getConfig(start).getUserLastActiveTable() + "(app_id);";
}
+ public static String getQueryToCreateLastActiveTimeIndexForUserLastActiveTable(Start start) {
+ return "CREATE INDEX IF NOT EXISTS user_last_active_last_active_time_index ON "
+ + Config.getConfig(start).getUserLastActiveTable() + "(last_active_time DESC, app_id DESC);";
+ }
+
public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime)
throws SQLException, StorageQueryException {
String QUERY = "SELECT COUNT(*) as total FROM " + Config.getConfig(start).getUserLastActiveTable()
@@ -50,7 +57,8 @@ public static int countUsersActiveSince(Start start, AppIdentifier appIdentifier
});
}
- public static int countUsersActiveSinceAndHasMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier, long sinceTime)
+ public static int countUsersActiveSinceAndHasMoreThanOneLoginMethod(Start start, AppIdentifier appIdentifier,
+ long sinceTime)
throws SQLException, StorageQueryException {
// TODO: Active users are present only on public tenant and MFA users may be present on different storages
String QUERY = "SELECT count(1) as c FROM ("
@@ -89,6 +97,22 @@ public static int updateUserLastActive(Start start, AppIdentifier appIdentifier,
});
}
+ @TestOnly
+ public static int updateUserLastActive(Start start, AppIdentifier appIdentifier, String userId, long timestamp)
+ throws SQLException, StorageQueryException {
+ String QUERY = "INSERT INTO " + Config.getConfig(start).getUserLastActiveTable()
+ +
+ "(app_id, user_id, last_active_time) VALUES(?, ?, ?) ON CONFLICT(app_id, user_id) DO UPDATE SET " +
+ "last_active_time = ?";
+
+ return update(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, userId);
+ pst.setLong(3, timestamp);
+ pst.setLong(4, timestamp);
+ });
+ }
+
public static Long getLastActiveByUserId(Start start, AppIdentifier appIdentifier, String userId)
throws StorageQueryException {
String QUERY = "SELECT last_active_time FROM " + Config.getConfig(start).getUserLastActiveTable()
@@ -121,30 +145,32 @@ public static void deleteUserActive_Transaction(Connection con, Start start, App
});
}
- public static int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(Start start, AppIdentifier appIdentifier, long sinceTime)
+ public static int countUsersThatHaveMoreThanOneLoginMethodOrTOTPEnabledAndActiveSince(Start start,
+ AppIdentifier appIdentifier,
+ long sinceTime)
throws SQLException, StorageQueryException {
// TODO: Active users are present only on public tenant and MFA users may be present on different storages
String QUERY =
- "SELECT COUNT (DISTINCT user_id) as c FROM ("
- + " (" // users with more than one login method
- + " SELECT primary_or_recipe_user_id AS user_id FROM ("
- + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id"
- + " FROM " + getConfig(start).getAppIdToUserIdTable()
- + " WHERE app_id = ? AND primary_or_recipe_user_id IN ("
- + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable()
- + " WHERE app_id = ? AND last_active_time >= ?"
- + " )"
- + " GROUP BY (app_id, primary_or_recipe_user_id)"
- + " ) AS nloginmethods"
- + " WHERE num_login_methods > 1"
- + " ) UNION (" // TOTP users
- + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable()
- + " WHERE app_id = ? AND user_id IN ("
- + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable()
- + " WHERE app_id = ? AND last_active_time >= ?"
- + " )"
- + " )"
- + ") AS all_users";
+ "SELECT COUNT (DISTINCT user_id) as c FROM ("
+ + " (" // users with more than one login method
+ + " SELECT primary_or_recipe_user_id AS user_id FROM ("
+ + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id"
+ + " FROM " + getConfig(start).getAppIdToUserIdTable()
+ + " WHERE app_id = ? AND primary_or_recipe_user_id IN ("
+ + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable()
+ + " WHERE app_id = ? AND last_active_time >= ?"
+ + " )"
+ + " GROUP BY (app_id, primary_or_recipe_user_id)"
+ + " ) AS nloginmethods"
+ + " WHERE num_login_methods > 1"
+ + " ) UNION (" // TOTP users
+ + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable()
+ + " WHERE app_id = ? AND user_id IN ("
+ + " SELECT user_id FROM " + getConfig(start).getUserLastActiveTable()
+ + " WHERE app_id = ? AND last_active_time >= ?"
+ + " )"
+ + " )"
+ + ") AS all_users";
return execute(start, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java
index 135d2f7a..89e85607 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/DashboardQueries.java
@@ -51,7 +51,7 @@ public static String getQueryToCreateDashboardUsersTable(Start start) {
+ " PRIMARY KEY (app_id, user_id),"
+ "CONSTRAINT " + Utils.getConstraintName(schema, dashboardUsersTable, "app_id", "fkey")
+ " FOREIGN KEY(app_id)"
- + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE"
+ ");";
// @formatter:on
}
@@ -119,7 +119,8 @@ public static boolean deleteDashboardUserWithUserId(Start start, AppIdentifier a
}
- public static DashboardUser[] getAllDashBoardUsers(Start start, AppIdentifier appIdentifier) throws SQLException, StorageQueryException {
+ public static DashboardUser[] getAllDashBoardUsers(Start start, AppIdentifier appIdentifier)
+ throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM "
+ Config.getConfig(start).getDashboardUsersTable() + " WHERE app_id = ? ORDER BY time_joined ASC";
return QueryExecutorTemplate.execute(start, QUERY,
@@ -172,8 +173,9 @@ public static boolean updateDashboardUsersPasswordWithUserId_Transaction(Start s
return rowsUpdated > 0;
}
- public static void createDashboardSession(Start start, AppIdentifier appIdentifier, String userId, String sessionId, long timeCreated,
- long expiry) throws SQLException, StorageQueryException {
+ public static void createDashboardSession(Start start, AppIdentifier appIdentifier, String userId, String sessionId,
+ long timeCreated,
+ long expiry) throws SQLException, StorageQueryException {
String QUERY = "INSERT INTO " + Config.getConfig(start).getDashboardSessionsTable()
+ "(app_id, user_id, session_id, time_created, expiry)" + " VALUES(?, ?, ?, ?, ?)";
QueryExecutorTemplate.update(start, QUERY, pst -> {
@@ -185,7 +187,8 @@ public static void createDashboardSession(Start start, AppIdentifier appIdentifi
});
}
- public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppIdentifier appIdentifier, String sessionId)
+ public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppIdentifier appIdentifier,
+ String sessionId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM "
+ Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND session_id = ?";
@@ -200,7 +203,8 @@ public static DashboardSessionInfo getSessionInfoWithSessionId(Start start, AppI
});
}
- public static DashboardSessionInfo[] getAllSessionsForUserId(Start start, AppIdentifier appIdentifier, String userId)
+ public static DashboardSessionInfo[] getAllSessionsForUserId(Start start, AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM "
+ Config.getConfig(start).getDashboardSessionsTable() + " WHERE app_id = ? AND user_id = ?";
@@ -234,7 +238,8 @@ public static DashboardUser getDashboardUserByEmail(Start start, AppIdentifier a
});
}
- public static boolean deleteDashboardUserSessionWithSessionId(Start start, AppIdentifier appIdentifier, String sessionId)
+ public static boolean deleteDashboardUserSessionWithSessionId(Start start, AppIdentifier appIdentifier,
+ String sessionId)
throws SQLException, StorageQueryException {
String QUERY = "DELETE FROM " + Config.getConfig(start).getDashboardSessionsTable()
+ " WHERE app_id = ? AND session_id = ?";
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java
index 55bb51c4..efed6c7f 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailPasswordQueries.java
@@ -285,7 +285,9 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden
{ // all_auth_recipe_users
String QUERY = "INSERT INTO " + getConfig(start).getUsersTable()
- + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" +
+ +
+ "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " +
+ "primary_or_recipe_user_time_joined)" +
" VALUES(?, ?, ?, ?, ?, ?, ?)";
update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
@@ -375,8 +377,10 @@ public static void deleteUser_Transaction(Connection sqlCon, Start start, AppIde
}
}
- private static UserInfoPartial getUserInfoUsingId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier,
- String id) throws SQLException, StorageQueryException {
+ private static UserInfoPartial getUserInfoUsingId_Transaction(Start start, Connection sqlCon,
+ AppIdentifier appIdentifier,
+ String id)
+ throws SQLException, StorageQueryException {
// we don't need a FOR UPDATE here because this is already part of a transaction, and locked on
// app_id_to_user_id table
String QUERY = "SELECT user_id, email, password_hash, time_joined FROM "
@@ -426,7 +430,7 @@ public static List getUsersInfoUsingIdList(Start start, Set
}
public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids,
- AppIdentifier appIdentifier)
+ AppIdentifier appIdentifier)
throws SQLException, StorageQueryException {
if (ids.size() > 0) {
// No need to filter based on tenantId because the id list is already filtered for a tenant
@@ -456,6 +460,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start,
}
return Collections.emptyList();
}
+
public static String lockEmail_Transaction(Start start, Connection con,
AppIdentifier appIdentifier,
String email)
@@ -475,7 +480,7 @@ public static String lockEmail_Transaction(Start start, Connection con,
}
public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier,
- String email)
+ String email)
throws StorageQueryException, SQLException {
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
+ "FROM " + getConfig(start).getEmailPasswordUserToTenantTable() + " AS ep" +
@@ -495,8 +500,9 @@ public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier te
});
}
- public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier,
- String email)
+ public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con,
+ AppIdentifier appIdentifier,
+ String email)
throws StorageQueryException, SQLException {
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
+ "FROM " + getConfig(start).getEmailPasswordUsersTable() + " AS ep" +
@@ -526,11 +532,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC
throw new UnknownUserIdException();
}
- GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId);
+ GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start,
+ sqlCon, tenantIdentifier.toAppIdentifier(), userId);
{ // all_auth_recipe_users
String QUERY = "INSERT INTO " + getConfig(start).getUsersTable()
- + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)"
+ +
+ "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " +
+ "recipe_id, time_joined, primary_or_recipe_user_time_joined)"
+ " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING";
GeneralQueries.AccountLinkingInfo finalAccountLinkingInfo = accountLinkingInfo;
@@ -545,14 +554,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC
pst.setLong(8, userInfo.timeJoined);
});
- GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), finalAccountLinkingInfo.primaryUserId);
+ GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(),
+ finalAccountLinkingInfo.primaryUserId);
}
{ // emailpassword_user_to_tenant
String QUERY = "INSERT INTO " + getConfig(start).getEmailPasswordUserToTenantTable()
+ "(app_id, tenant_id, user_id, email)"
+ " VALUES(?, ?, ?, ?) " + " ON CONFLICT ON CONSTRAINT "
- + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getEmailPasswordUserToTenantTable(), null, "pkey")
+ + Utils.getConstraintName(Config.getConfig(start).getTableSchema(),
+ getConfig(start).getEmailPasswordUserToTenantTable(), null, "pkey")
+ " DO NOTHING";
int numRows = update(sqlCon, QUERY, pst -> {
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java
index 6fd00660..9c70cf8f 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/EmailVerificationQueries.java
@@ -270,8 +270,10 @@ public static List isEmailVerified_transaction(Start start, Connection s
// We have external user id stored in the email verification table, so we need to fetch the mapped userids for
// calculating the verified emails
- HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds_Transaction(start,
- sqlCon, appIdentifier, supertokensUserIds);
+ HashMap supertokensUserIdToExternalUserIdMap =
+ UserIdMappingQueries.getUserIdMappingWithUserIds_Transaction(
+ start,
+ sqlCon, appIdentifier, supertokensUserIds);
HashMap externalUserIdToSupertokensUserIdMap = new HashMap<>();
List supertokensOrExternalUserIdsToQuery = new ArrayList<>();
@@ -298,7 +300,8 @@ public static List isEmailVerified_transaction(Start start, Connection s
}
String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable()
- + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) +
+ + " WHERE app_id = ? AND user_id IN (" +
+ Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) +
") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")";
return execute(sqlCon, QUERY, pst -> {
@@ -324,7 +327,7 @@ public static List isEmailVerified_transaction(Start start, Connection s
}
public static List isEmailVerified(Start start, AppIdentifier appIdentifier,
- List userIdAndEmail)
+ List userIdAndEmail)
throws SQLException, StorageQueryException {
if (userIdAndEmail.isEmpty()) {
@@ -339,7 +342,8 @@ public static List isEmailVerified(Start start, AppIdentifier appIdentif
}
// We have external user id stored in the email verification table, so we need to fetch the mapped userids for
// calculating the verified emails
- HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds(start,
+ HashMap supertokensUserIdToExternalUserIdMap = UserIdMappingQueries.getUserIdMappingWithUserIds(
+ start,
appIdentifier, supertokensUserIds);
HashMap externalUserIdToSupertokensUserIdMap = new HashMap<>();
List supertokensOrExternalUserIdsToQuery = new ArrayList<>();
@@ -365,7 +369,8 @@ public static List isEmailVerified(Start start, AppIdentifier appIdentif
supertokensOrExternalUserIdToEmailMap.put(supertokensOrExternalUserId, ue.email);
}
String QUERY = "SELECT * FROM " + getConfig(start).getEmailVerificationTable()
- + " WHERE app_id = ? AND user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) +
+ + " WHERE app_id = ? AND user_id IN (" +
+ Utils.generateCommaSeperatedQuestionMarks(supertokensOrExternalUserIdsToQuery.size()) +
") AND email IN (" + Utils.generateCommaSeperatedQuestionMarks(emails.size()) + ")";
return execute(start, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
@@ -476,7 +481,8 @@ public static boolean isUserIdBeingUsedForEmailVerification(Start start, AppIden
}
}
- public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentifier appIdentifier, String supertokensUserId, String externalUserId)
+ public static void updateIsEmailVerifiedToExternalUserId(Start start, AppIdentifier appIdentifier,
+ String supertokensUserId, String externalUserId)
throws StorageQueryException {
try {
start.startTransaction((TransactionConnection con) -> {
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java
index d0d9f7de..8ac6aa8c 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/GeneralQueries.java
@@ -56,13 +56,15 @@
public class GeneralQueries {
- private static boolean doesTableExists(Start start, Connection connection, String tableName) throws SQLException, StorageQueryException {
+ private static boolean doesTableExists(Start start, Connection connection, String tableName)
+ throws SQLException, StorageQueryException {
try {
String QUERY = "SELECT 1 FROM " + tableName + " LIMIT 1";
execute(connection, QUERY, NO_OP_SETTER, result -> null);
return true;
} catch (SQLException | StorageQueryException e) {
- if (e.getMessage().contains("relation") && e.getMessage().contains(tableName) && e.getMessage().contains("does not exist")) {
+ if (e.getMessage().contains("relation") && e.getMessage().contains(tableName) &&
+ e.getMessage().contains("does not exist")) {
return false;
}
throw e;
@@ -89,7 +91,8 @@ static String getQueryToCreateUsersTable(Start start) {
+ " REFERENCES " + Config.getConfig(start).getTenantsTable() + " (app_id, tenant_id) ON DELETE CASCADE,"
+ "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "primary_or_recipe_user_id", "fkey")
+ " FOREIGN KEY(app_id, primary_or_recipe_user_id)"
- + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE,"
+ + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() +
+ " (app_id, user_id) ON DELETE CASCADE,"
+ "CONSTRAINT " + Utils.getConstraintName(schema, usersTable, "user_id", "fkey")
+ " FOREIGN KEY(app_id, user_id)"
+ " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() +
@@ -100,8 +103,16 @@ static String getQueryToCreateUsersTable(Start start) {
public static String getQueryToCreateUserIdIndexForUsersTable(Start start) {
return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_index ON "
+ + Config.getConfig(start).getUsersTable() + "(user_id);";
+ }
+ public static String getQueryToCreateUserIdAppIdIndexForUsersTable(Start start) {
+ return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_id_app_id_index ON "
+ Config.getConfig(start).getUsersTable() + "(app_id, user_id);";
}
+ public static String getQueryToCreateAppIdIndexForUsersTable(Start start) {
+ return "CREATE INDEX IF NOT EXISTS all_auth_recipe_user_app_id_index ON "
+ + Config.getConfig(start).getUsersTable() + "(app_id);";
+ }
public static String getQueryToCreateTenantIdIndexForUsersTable(Start start) {
return "CREATE INDEX IF NOT EXISTS all_auth_recipe_tenant_id_index ON "
@@ -120,12 +131,16 @@ static String getQueryToCreateUserPaginationIndex2(Start start) {
static String getQueryToCreateUserPaginationIndex3(Start start) {
return "CREATE INDEX all_auth_recipe_users_pagination_index3 ON " + Config.getConfig(start).getUsersTable()
- + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id DESC);";
+ +
+ "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined DESC, primary_or_recipe_user_id " +
+ "DESC);";
}
static String getQueryToCreateUserPaginationIndex4(Start start) {
return "CREATE INDEX all_auth_recipe_users_pagination_index4 ON " + Config.getConfig(start).getUsersTable()
- + "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id DESC);";
+ +
+ "(recipe_id, app_id, tenant_id, primary_or_recipe_user_time_joined ASC, primary_or_recipe_user_id " +
+ "DESC);";
}
static String getQueryToCreatePrimaryUserId(Start start) {
@@ -221,7 +236,8 @@ private static String getQueryToCreateAppIdToUserIdTable(Start start) {
+ " PRIMARY KEY (app_id, user_id), "
+ "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, "primary_or_recipe_user_id", "fkey")
+ " FOREIGN KEY(app_id, primary_or_recipe_user_id)"
- + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + " (app_id, user_id) ON DELETE CASCADE,"
+ + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() +
+ " (app_id, user_id) ON DELETE CASCADE,"
+ "CONSTRAINT " + Utils.getConstraintName(schema, appToUserTable, "app_id", "fkey")
+ " FOREIGN KEY(app_id) REFERENCES " + Config.getConfig(start).getAppsTable()
+ " (app_id) ON DELETE CASCADE"
@@ -239,6 +255,11 @@ static String getQueryToCreatePrimaryUserIdIndexForAppIdToUserIdTable(Start star
+ Config.getConfig(start).getAppIdToUserIdTable() + "(primary_or_recipe_user_id, app_id);";
}
+ static String getQueryToCreateUserIdIndexForAppIdToUserIdTable(Start start) {
+ return "CREATE INDEX IF NOT EXISTS app_id_to_user_id_user_id_index ON "
+ + Config.getConfig(start).getAppIdToUserIdTable() + "(user_id, app_id);";
+ }
+
public static void createTablesIfNotExists(Start start, Connection con) throws SQLException, StorageQueryException {
int numberOfRetries = 0;
boolean retry = true;
@@ -273,6 +294,7 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S
// index
update(con, getQueryToCreateAppIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER);
update(con, getQueryToCreatePrimaryUserIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER);
+ update(con, getQueryToCreateUserIdIndexForAppIdToUserIdTable(start), NO_OP_SETTER);
}
if (!doesTableExists(start, con, Config.getConfig(start).getUsersTable())) {
@@ -295,6 +317,8 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S
// Index
update(con, ActiveUsersQueries.getQueryToCreateAppIdIndexForUserLastActiveTable(start),
NO_OP_SETTER);
+ update(con, ActiveUsersQueries.getQueryToCreateLastActiveTimeIndexForUserLastActiveTable(start),
+ NO_OP_SETTER);
}
if (!doesTableExists(start, con, Config.getConfig(start).getAccessTokenSigningKeysTable())) {
@@ -423,6 +447,8 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S
// index
update(con, getQueryToCreateUserIdIndexForUsersTable(start), NO_OP_SETTER);
+ update(con, getQueryToCreateUserIdAppIdIndexForUsersTable(start), NO_OP_SETTER);
+ update(con, getQueryToCreateAppIdIndexForUsersTable(start), NO_OP_SETTER);
update(con, getQueryToCreateTenantIdIndexForUsersTable(start), NO_OP_SETTER);
}
@@ -553,6 +579,37 @@ public static void createTablesIfNotExists(Start start, Connection con) throws S
update(start, BulkImportQueries.getQueryToCreatePaginationIndex2(start), NO_OP_SETTER);
}
+ if (!doesTableExists(start, con, Config.getConfig(start).getOAuthClientsTable())) {
+ getInstance(start).addState(CREATING_NEW_TABLE, null);
+ update(con, OAuthQueries.getQueryToCreateOAuthClientTable(start), NO_OP_SETTER);
+ }
+
+ if (!doesTableExists(start, con, Config.getConfig(start).getOAuthSessionsTable())) {
+ getInstance(start).addState(CREATING_NEW_TABLE, null);
+ update(con, OAuthQueries.getQueryToCreateOAuthSessionsTable(start), NO_OP_SETTER);
+
+ // index
+ update(con, OAuthQueries.getQueryToCreateOAuthSessionsExpIndex(start), NO_OP_SETTER);
+ update(con, OAuthQueries.getQueryToCreateOAuthSessionsExternalRefreshTokenIndex(start), NO_OP_SETTER);
+ }
+
+ if (!doesTableExists(start, con, Config.getConfig(start).getOAuthM2MTokensTable())) {
+ getInstance(start).addState(CREATING_NEW_TABLE, null);
+ update(con, OAuthQueries.getQueryToCreateOAuthM2MTokensTable(start), NO_OP_SETTER);
+
+ // index
+ update(con, OAuthQueries.getQueryToCreateOAuthM2MTokenIatIndex(start), NO_OP_SETTER);
+ update(con, OAuthQueries.getQueryToCreateOAuthM2MTokenExpIndex(start), NO_OP_SETTER);
+ }
+
+ if (!doesTableExists(start, con, Config.getConfig(start).getOAuthLogoutChallengesTable())) {
+ getInstance(start).addState(CREATING_NEW_TABLE, null);
+ update(con, OAuthQueries.getQueryToCreateOAuthLogoutChallengesTable(start), NO_OP_SETTER);
+
+ // index
+ update(con, OAuthQueries.getQueryToCreateOAuthLogoutChallengesTimeCreatedIndex(start), NO_OP_SETTER);
+ }
+
} catch (Exception e) {
if (e.getMessage().contains("schema") && e.getMessage().contains("does not exist")
&& numberOfRetries < 1) {
@@ -634,10 +691,18 @@ public static void deleteAllTables(Start start) throws SQLException, StorageQuer
+ getConfig(start).getUserRolesTable() + ","
+ getConfig(start).getDashboardUsersTable() + ","
+ getConfig(start).getDashboardSessionsTable() + ","
- + getConfig(start).getTotpUsedCodesTable() + ","
+ + getConfig(start).getOAuthClientsTable() + ","
+ + getConfig(start).getOAuthM2MTokensTable() + ","
+ + getConfig(start).getOAuthLogoutChallengesTable() + ","
+ + getConfig(start).getTotpUsedCodesTable() + ","
+ getConfig(start).getTotpUserDevicesTable() + ","
+ getConfig(start).getTotpUsersTable() + ","
- + getConfig(start).getBulkImportUsersTable();
+ + getConfig(start).getBulkImportUsersTable() + ","
+ + getConfig(start).getOAuthClientsTable() + ","
+ + getConfig(start).getOAuthSessionsTable() + ","
+ + getConfig(start).getOAuthLogoutChallengesTable() + ","
+ + getConfig(start).getOAuthM2MTokensTable();
+
update(start, DROP_QUERY, NO_OP_SETTER);
}
}
@@ -718,8 +783,8 @@ public static void deleteKeyValue_Transaction(Start start, Connection con, Tenan
public static long getUsersCount(Start start, AppIdentifier appIdentifier, RECIPE_ID[] includeRecipeIds)
throws SQLException, StorageQueryException {
StringBuilder QUERY = new StringBuilder(
- "SELECT COUNT(DISTINCT primary_or_recipe_user_id) AS total FROM " +
- getConfig(start).getUsersTable());
+ "SELECT COUNT(*) AS total FROM (");
+ QUERY.append("SELECT primary_or_recipe_user_id FROM " + getConfig(start).getUsersTable());
QUERY.append(" WHERE app_id = ?");
if (includeRecipeIds != null && includeRecipeIds.length > 0) {
QUERY.append(" AND recipe_id IN (");
@@ -732,6 +797,7 @@ public static long getUsersCount(Start start, AppIdentifier appIdentifier, RECIP
}
QUERY.append(")");
}
+ QUERY.append(" GROUP BY primary_or_recipe_user_id) AS uniq_users");
return execute(start, QUERY.toString(), pst -> {
pst.setString(1, appIdentifier.getAppId());
@@ -752,7 +818,8 @@ public static long getUsersCount(Start start, AppIdentifier appIdentifier, RECIP
public static long getUsersCount(Start start, TenantIdentifier tenantIdentifier, RECIPE_ID[] includeRecipeIds)
throws SQLException, StorageQueryException {
StringBuilder QUERY = new StringBuilder(
- "SELECT COUNT(DISTINCT primary_or_recipe_user_id) AS total FROM " + getConfig(start).getUsersTable());
+ "SELECT COUNT(*) AS total FROM (");
+ QUERY.append("SELECT primary_or_recipe_user_id FROM " + getConfig(start).getUsersTable());
QUERY.append(" WHERE app_id = ? AND tenant_id = ?");
if (includeRecipeIds != null && includeRecipeIds.length > 0) {
QUERY.append(" AND recipe_id IN (");
@@ -766,6 +833,8 @@ public static long getUsersCount(Start start, TenantIdentifier tenantIdentifier,
QUERY.append(")");
}
+ QUERY.append(" GROUP BY primary_or_recipe_user_id) AS uniq_users");
+
return execute(start, QUERY.toString(), pst -> {
pst.setString(1, tenantIdentifier.getAppId());
pst.setString(2, tenantIdentifier.getTenantId());
@@ -795,7 +864,8 @@ public static boolean doesUserIdExist(Start start, AppIdentifier appIdentifier,
}, ResultSet::next);
}
- public static boolean doesUserIdExist_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId)
+ public static boolean doesUserIdExist_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
// We query both tables cause there is a case where a primary user ID exists, but its associated
// recipe user ID has been deleted AND there are other recipe user IDs linked to this primary user ID already.
@@ -1007,8 +1077,11 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant
usersFromQuery = new ArrayList<>();
} else {
- String finalQuery = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM ( " + USER_SEARCH_TAG_CONDITION.toString() + " )"
- + " AS finalResultTable ORDER BY primary_or_recipe_user_time_joined " + timeJoinedOrder + ", primary_or_recipe_user_id DESC ";
+ String finalQuery =
+ "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM ( " +
+ USER_SEARCH_TAG_CONDITION.toString() + " )"
+ + " AS finalResultTable ORDER BY primary_or_recipe_user_time_joined " +
+ timeJoinedOrder + ", primary_or_recipe_user_id DESC ";
usersFromQuery = execute(start, finalQuery, pst -> {
for (int i = 1; i <= queryList.size(); i++) {
pst.setString(i, queryList.get(i - 1));
@@ -1044,9 +1117,12 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant
recipeIdCondition = recipeIdCondition + " AND";
}
String timeJoinedOrderSymbol = timeJoinedOrder.equals("ASC") ? ">" : "<";
- String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + getConfig(start).getUsersTable() + " WHERE "
+ String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " +
+ getConfig(start).getUsersTable() + " WHERE "
+ recipeIdCondition + " (primary_or_recipe_user_time_joined " + timeJoinedOrderSymbol
- + " ? OR (primary_or_recipe_user_time_joined = ? AND primary_or_recipe_user_id <= ?)) AND app_id = ? AND tenant_id = ?"
+ +
+ " ? OR (primary_or_recipe_user_time_joined = ? AND primary_or_recipe_user_id <= ?)) AND " +
+ "app_id = ? AND tenant_id = ?"
+ " ORDER BY primary_or_recipe_user_time_joined " + timeJoinedOrder
+ ", primary_or_recipe_user_id DESC LIMIT ?";
usersFromQuery = execute(start, QUERY, pst -> {
@@ -1072,7 +1148,8 @@ public static AuthRecipeUserInfo[] getUsers(Start start, TenantIdentifier tenant
});
} else {
String recipeIdCondition = RECIPE_ID_CONDITION.toString();
- String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " + getConfig(start).getUsersTable() + " WHERE ";
+ String QUERY = "SELECT DISTINCT primary_or_recipe_user_id, primary_or_recipe_user_time_joined FROM " +
+ getConfig(start).getUsersTable() + " WHERE ";
if (!recipeIdCondition.equals("")) {
QUERY += recipeIdCondition + " AND";
}
@@ -1258,7 +1335,8 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByThirdPartyInfo_Transaction(
// now that we have locks on all the relevant tables, we can read from them safely
List userIds = ThirdPartyQueries.listUserIdsByThirdPartyInfo_Transaction(start, sqlCon, appIdentifier,
thirdPartyId, thirdPartyUserId);
- List result = getPrimaryUserInfoForUserIds_Transaction(start, sqlCon, appIdentifier, userIds);
+ List result = getPrimaryUserInfoForUserIds_Transaction(start, sqlCon, appIdentifier,
+ userIds);
// this is going to order them based on oldest that joined to newest that joined.
result.sort(Comparator.comparingLong(o -> o.timeJoined));
@@ -1356,9 +1434,9 @@ public static AuthRecipeUserInfo[] listPrimaryUsersByPhoneNumber(Start start,
}
public static AuthRecipeUserInfo getPrimaryUserByThirdPartyInfo(Start start,
- TenantIdentifier tenantIdentifier,
- String thirdPartyId,
- String thirdPartyUserId)
+ TenantIdentifier tenantIdentifier,
+ String thirdPartyId,
+ String thirdPartyUserId)
throws StorageQueryException, SQLException {
String userId = ThirdPartyQueries.getUserIdByThirdPartyInfo(start, tenantIdentifier,
thirdPartyId, thirdPartyUserId);
@@ -1392,7 +1470,7 @@ public static AuthRecipeUserInfo getPrimaryUserInfoForUserId(Start start, AppIde
}
public static AuthRecipeUserInfo getPrimaryUserInfoForUserId_Transaction(Start start, Connection con,
- AppIdentifier appIdentifier, String id)
+ AppIdentifier appIdentifier, String id)
throws SQLException, StorageQueryException {
List ids = new ArrayList<>();
ids.add(id);
@@ -1415,14 +1493,18 @@ private static List getPrimaryUserInfoForUserIds(Start start
// which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column,
// or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id
// column
- String QUERY = "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, aaru.tenant_id, aaru.time_joined FROM " + getConfig(start).getAppIdToUserIdTable() + " as au " +
- "LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" +
- " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " +
- getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN ("
- + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
- ") OR primary_or_recipe_user_id IN (" +
- Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
- ")) AND app_id = ?) AND au.app_id = ?";
+ String QUERY =
+ "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " +
+ "aaru.tenant_id, aaru.time_joined FROM " +
+ getConfig(start).getAppIdToUserIdTable() + " as au " +
+ "LEFT JOIN " + getConfig(start).getUsersTable() +
+ " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" +
+ " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " +
+ getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN ("
+ + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
+ ") OR primary_or_recipe_user_id IN (" +
+ Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
+ ")) AND app_id = ?) AND au.app_id = ?";
List allAuthUsersResult = execute(start, QUERY, pst -> {
// IN user_id
@@ -1496,8 +1578,8 @@ private static List getPrimaryUserInfoForUserIds(Start start
}
private static List getPrimaryUserInfoForUserIds_Transaction(Start start, Connection sqlCon,
- AppIdentifier appIdentifier,
- List userIds)
+ AppIdentifier appIdentifier,
+ List userIds)
throws StorageQueryException, SQLException {
if (userIds.size() == 0) {
return new ArrayList<>();
@@ -1507,14 +1589,37 @@ private static List getPrimaryUserInfoForUserIds_Transaction
// which is linked to a primary user ID in which case it won't be in the primary_or_recipe_user_id column,
// or the input may have a primary user ID whose recipe user ID was removed, so it won't be in the user_id
// column
- String QUERY = "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, aaru.tenant_id, aaru.time_joined FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" +
- " LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" +
- " WHERE au.primary_or_recipe_user_id IN (SELECT primary_or_recipe_user_id FROM " +
- getConfig(start).getAppIdToUserIdTable() + " WHERE (user_id IN ("
- + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
- ") OR primary_or_recipe_user_id IN (" +
- Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +
- ")) AND app_id = ?) AND au.app_id = ?";
+// String QUERY =
+// "SELECT au.user_id, au.primary_or_recipe_user_id, au.is_linked_or_is_a_primary_user, au.recipe_id, " +
+// "aaru.tenant_id, aaru.time_joined " +
+// "FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" +
+// " LEFT JOIN " + getConfig(start).getUsersTable() +
+// " as aaru ON au.app_id = aaru.app_id AND au.user_id = aaru.user_id" +
+// " WHERE au.primary_or_recipe_user_id IN " +
+// " (SELECT primary_or_recipe_user_id FROM " +
+// getConfig(start).getAppIdToUserIdTable() +
+// " WHERE (user_id IN ("
+// + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +") " +
+// " OR primary_or_recipe_user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")) " +
+// " AND app_id = ?) " +
+// "AND au.app_id = ?";
+
+ String QUERY = "SELECT" +
+ " au.user_id," +
+ " au.primary_or_recipe_user_id," +
+ " au.is_linked_or_is_a_primary_user," +
+ " au.recipe_id," +
+ " aaru.tenant_id," +
+ " aaru.time_joined" +
+ " FROM " + getConfig(start).getAppIdToUserIdTable() + " as au" +
+ " LEFT JOIN " + getConfig(start).getUsersTable() + " as aaru ON au.app_id = aaru.app_id" +
+ " AND au.user_id = aaru.user_id" +
+ " LEFT JOIN " + getConfig(start).getAppIdToUserIdTable() + " as aiui ON au.primary_or_recipe_user_id = aiui.user_id" +
+ " AND aiui.app_id = au.app_id" +
+ " WHERE" +
+ " aiui.user_id IN (" + Utils.generateCommaSeperatedQuestionMarks(userIds.size()) + ")" +
+ " OR au.primary_or_recipe_user_id IN ("+ Utils.generateCommaSeperatedQuestionMarks(userIds.size()) +")" +
+ " AND au.app_id = ?";
List allAuthUsersResult = execute(sqlCon, QUERY, pst -> {
// IN user_id
@@ -1528,7 +1633,7 @@ private static List getPrimaryUserInfoForUserIds_Transaction
}
// for app_id
pst.setString(index, appIdentifier.getAppId());
- pst.setString(index + 1, appIdentifier.getAppId());
+// System.out.println(pst);
}, result -> {
List parsedResult = new ArrayList<>();
while (result.next()) {
@@ -1551,10 +1656,13 @@ private static List getPrimaryUserInfoForUserIds_Transaction
List loginMethods = new ArrayList<>();
loginMethods.addAll(
- EmailPasswordQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier));
- loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier));
+ EmailPasswordQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch,
+ appIdentifier));
+ loginMethods.addAll(ThirdPartyQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch,
+ appIdentifier));
loginMethods.addAll(
- PasswordlessQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch, appIdentifier));
+ PasswordlessQueries.getUsersInfoUsingIdList_Transaction(start, sqlCon, recipeUserIdsToFetch,
+ appIdentifier));
Map recipeUserIdToLoginMethodMap = new HashMap<>();
for (LoginMethod loginMethod : loginMethods) {
@@ -1753,20 +1861,20 @@ public static int getUsersCountWithMoreThanOneLoginMethod(Start start, AppIdenti
public static int getUsersCountWithMoreThanOneLoginMethodOrTOTPEnabled(Start start, AppIdentifier appIdentifier)
throws SQLException, StorageQueryException {
String QUERY =
- "SELECT COUNT (DISTINCT user_id) as c FROM ("
- + " (" // Users with number of login methods > 1
- + " SELECT primary_or_recipe_user_id AS user_id FROM ("
- + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id"
- + " FROM " + getConfig(start).getAppIdToUserIdTable()
- + " WHERE app_id = ? "
- + " GROUP BY (app_id, primary_or_recipe_user_id)"
- + " ) AS nloginmethods"
- + " WHERE num_login_methods > 1"
- + " ) UNION (" // TOTP users
- + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable()
- + " WHERE app_id = ?"
- + " )"
- + ") AS all_users";
+ "SELECT COUNT (DISTINCT user_id) as c FROM ("
+ + " (" // Users with number of login methods > 1
+ + " SELECT primary_or_recipe_user_id AS user_id FROM ("
+ + " SELECT COUNT(user_id) as num_login_methods, app_id, primary_or_recipe_user_id"
+ + " FROM " + getConfig(start).getAppIdToUserIdTable()
+ + " WHERE app_id = ? "
+ + " GROUP BY (app_id, primary_or_recipe_user_id)"
+ + " ) AS nloginmethods"
+ + " WHERE num_login_methods > 1"
+ + " ) UNION (" // TOTP users
+ + " SELECT user_id FROM " + getConfig(start).getTotpUsersTable()
+ + " WHERE app_id = ?"
+ + " )"
+ + ") AS all_users";
return execute(start, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
@@ -1789,7 +1897,8 @@ public static boolean checkIfUsesAccountLinking(Start start, AppIdentifier appId
});
}
- public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId)
+ public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start, Connection sqlCon,
+ AppIdentifier appIdentifier, String userId)
throws SQLException, StorageQueryException {
GeneralQueries.AccountLinkingInfo accountLinkingInfo = new GeneralQueries.AccountLinkingInfo(userId, false);
{
@@ -1811,7 +1920,8 @@ public static AccountLinkingInfo getAccountLinkingInfo_Transaction(Start start,
return accountLinkingInfo;
}
- public static void updateTimeJoinedForPrimaryUser_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String primaryUserId)
+ public static void updateTimeJoinedForPrimaryUser_Transaction(Start start, Connection sqlCon,
+ AppIdentifier appIdentifier, String primaryUserId)
throws SQLException, StorageQueryException {
String QUERY = "UPDATE " + getConfig(start).getUsersTable() +
" SET primary_or_recipe_user_time_joined = (SELECT MIN(time_joined) FROM " +
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java
index f57c4402..9e80fe48 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/JWTSigningQueries.java
@@ -52,14 +52,14 @@ static String getQueryToCreateJWTSigningTable(Start start) {
return "CREATE TABLE IF NOT EXISTS " + jwtSigningKeysTable + " ("
+ "app_id VARCHAR(64) DEFAULT 'public',"
+ "key_id VARCHAR(255) NOT NULL,"
- + "key_string TEXT NOT NULL,"
+ + "key_string TEXT NOT NULL,"
+ "algorithm VARCHAR(10) NOT NULL,"
- + "created_at BIGINT,"
+ + "created_at BIGINT,"
+ "CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, null, "pkey")
+ " PRIMARY KEY(app_id, key_id),"
+ "CONSTRAINT " + Utils.getConstraintName(schema, jwtSigningKeysTable, "app_id", "fkey")
+ " FOREIGN KEY(app_id)"
- + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE"
+ ");";
// @formatter:on
}
@@ -75,7 +75,7 @@ public static List getJWTSigningKeys_Transaction(Start start,
String QUERY = "SELECT * FROM " + getConfig(start).getJWTSigningKeysTable()
+ " WHERE app_id = ? ORDER BY created_at DESC FOR UPDATE";
- return execute(con, QUERY, pst -> pst.setString(1, appIdentifier.getAppId()),result -> {
+ return execute(con, QUERY, pst -> pst.setString(1, appIdentifier.getAppId()), result -> {
List keys = new ArrayList<>();
while (result.next()) {
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java
index 0d9bf826..b0c7ffe1 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/MultitenancyQueries.java
@@ -18,7 +18,9 @@
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.exceptions.StorageTransactionLogicException;
-import io.supertokens.pluginInterface.multitenancy.*;
+import io.supertokens.pluginInterface.multitenancy.TenantConfig;
+import io.supertokens.pluginInterface.multitenancy.TenantIdentifier;
+import io.supertokens.pluginInterface.multitenancy.ThirdPartyConfig;
import io.supertokens.pluginInterface.multitenancy.exceptions.TenantOrAppNotFoundException;
import io.supertokens.storage.postgresql.Start;
import io.supertokens.storage.postgresql.config.Config;
@@ -32,8 +34,8 @@
import java.sql.SQLException;
import java.util.HashMap;
-import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update;
import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
+import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update;
import static io.supertokens.storage.postgresql.config.Config.getConfig;
public class MultitenancyQueries {
@@ -51,7 +53,9 @@ static String getQueryToCreateTenantConfigsTable(Start start) {
+ "email_password_enabled BOOLEAN,"
+ "passwordless_enabled BOOLEAN,"
+ "third_party_enabled BOOLEAN,"
- + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)"
+ + "is_first_factors_null BOOLEAN,"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, tenantConfigsTable, null, "pkey") +
+ " PRIMARY KEY (connection_uri_domain, app_id, tenant_id)"
+ ");";
// @formatter:on
}
@@ -82,10 +86,12 @@ static String getQueryToCreateTenantThirdPartyProvidersTable(Start start) {
+ "user_info_map_from_user_info_endpoint_user_id VARCHAR(64),"
+ "user_info_map_from_user_info_endpoint_email VARCHAR(64),"
+ "user_info_map_from_user_info_endpoint_email_verified VARCHAR(64),"
- + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id),"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, null, "pkey") +
+ " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id),"
+ "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, "tenant_id", "fkey")
+ " FOREIGN KEY(connection_uri_domain, app_id, tenant_id)"
- + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() +
+ " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE"
+ ");";
// @formatter:on
}
@@ -109,16 +115,20 @@ static String getQueryToCreateTenantThirdPartyProviderClientsTable(Start start)
+ "scope VARCHAR(128)[],"
+ "force_pkce BOOLEAN,"
+ "additional_config TEXT,"
- + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, null, "pkey") + " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id, client_type),"
- + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, "third_party_id", "fkey")
+ + "CONSTRAINT " + Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, null, "pkey") +
+ " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, third_party_id, client_type),"
+ + "CONSTRAINT " +
+ Utils.getConstraintName(schema, tenantThirdPartyProvidersTable, "third_party_id", "fkey")
+ " FOREIGN KEY(connection_uri_domain, app_id, tenant_id, third_party_id)"
- + " REFERENCES " + Config.getConfig(start).getTenantThirdPartyProvidersTable() + " (connection_uri_domain, app_id, tenant_id, third_party_id) ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getTenantThirdPartyProvidersTable() +
+ " (connection_uri_domain, app_id, tenant_id, third_party_id) ON DELETE CASCADE"
+ ");";
}
public static String getQueryToCreateThirdPartyIdIndexForTenantThirdPartyProviderClientsTable(Start start) {
return "CREATE INDEX IF NOT EXISTS tenant_thirdparty_provider_clients_third_party_id_index ON "
- + getConfig(start).getTenantThirdPartyProviderClientsTable() + " (connection_uri_domain, app_id, tenant_id, third_party_id);";
+ + getConfig(start).getTenantThirdPartyProviderClientsTable() +
+ " (connection_uri_domain, app_id, tenant_id, third_party_id);";
}
public static String getQueryToCreateFirstFactorsTable(Start start) {
@@ -134,7 +144,8 @@ public static String getQueryToCreateFirstFactorsTable(Start start) {
+ " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, factor_id),"
+ "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "tenant_id", "fkey")
+ " FOREIGN KEY (connection_uri_domain, app_id, tenant_id)"
- + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() +
+ " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE"
+ ");";
// @formatter:on
}
@@ -157,14 +168,16 @@ public static String getQueryToCreateRequiredSecondaryFactorsTable(Start start)
+ " PRIMARY KEY (connection_uri_domain, app_id, tenant_id, factor_id),"
+ "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "tenant_id", "fkey")
+ " FOREIGN KEY (connection_uri_domain, app_id, tenant_id)"
- + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() + " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getTenantConfigsTable() +
+ " (connection_uri_domain, app_id, tenant_id) ON DELETE CASCADE"
+ ");";
// @formatter:on
}
public static String getQueryToCreateTenantIdIndexForRequiredSecondaryFactorsTable(Start start) {
return "CREATE INDEX IF NOT EXISTS tenant_default_required_factor_ids_tenant_id_index ON "
- + getConfig(start).getTenantRequiredSecondaryFactorsTable() + " (connection_uri_domain, app_id, tenant_id);";
+ + getConfig(start).getTenantRequiredSecondaryFactorsTable() +
+ " (connection_uri_domain, app_id, tenant_id);";
}
@@ -173,19 +186,23 @@ private static void executeCreateTenantQueries(Start start, Connection sqlCon, T
TenantConfigSQLHelper.create(start, sqlCon, tenantConfig);
- for (ThirdPartyConfig.Provider provider : tenantConfig.thirdPartyConfig.providers) {
- ThirdPartyProviderSQLHelper.create(start, sqlCon, tenantConfig, provider);
+ if (tenantConfig.thirdPartyConfig.providers != null) {
+ for (ThirdPartyConfig.Provider provider : tenantConfig.thirdPartyConfig.providers) {
+ ThirdPartyProviderSQLHelper.create(start, sqlCon, tenantConfig, provider);
- for (ThirdPartyConfig.ProviderClient providerClient : provider.clients) {
- ThirdPartyProviderClientSQLHelper.create(start, sqlCon, tenantConfig, provider, providerClient);
+ for (ThirdPartyConfig.ProviderClient providerClient : provider.clients) {
+ ThirdPartyProviderClientSQLHelper.create(start, sqlCon, tenantConfig, provider, providerClient);
+ }
}
}
MfaSqlHelper.createFirstFactors(start, sqlCon, tenantConfig.tenantIdentifier, tenantConfig.firstFactors);
- MfaSqlHelper.createRequiredSecondaryFactors(start, sqlCon, tenantConfig.tenantIdentifier, tenantConfig.requiredSecondaryFactors);
+ MfaSqlHelper.createRequiredSecondaryFactors(start, sqlCon, tenantConfig.tenantIdentifier,
+ tenantConfig.requiredSecondaryFactors);
}
- public static void createTenantConfig(Start start, TenantConfig tenantConfig) throws StorageQueryException, StorageTransactionLogicException {
+ public static void createTenantConfig(Start start, TenantConfig tenantConfig)
+ throws StorageQueryException, StorageTransactionLogicException {
start.startTransaction(con -> {
Connection sqlCon = (Connection) con.getConnection();
{
@@ -201,7 +218,8 @@ public static void createTenantConfig(Start start, TenantConfig tenantConfig) th
});
}
- public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIdentifier) throws StorageQueryException {
+ public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIdentifier)
+ throws StorageQueryException {
try {
String QUERY = "DELETE FROM " + getConfig(start).getTenantConfigsTable()
+ " WHERE connection_uri_domain = ? AND app_id = ? AND tenant_id = ?";
@@ -219,7 +237,8 @@ public static boolean deleteTenantConfig(Start start, TenantIdentifier tenantIde
}
}
- public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig) throws StorageQueryException, StorageTransactionLogicException {
+ public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig)
+ throws StorageQueryException, StorageTransactionLogicException {
start.startTransaction(con -> {
Connection sqlCon = (Connection) con.getConnection();
{
@@ -233,7 +252,8 @@ public static void overwriteTenantConfig(Start start, TenantConfig tenantConfig)
pst.setString(3, tenantConfig.tenantIdentifier.getTenantId());
});
if (rowsAffected == 0) {
- throw new StorageTransactionLogicException(new TenantOrAppNotFoundException(tenantConfig.tenantIdentifier));
+ throw new StorageTransactionLogicException(
+ new TenantOrAppNotFoundException(tenantConfig.tenantIdentifier));
}
}
@@ -256,16 +276,21 @@ public static TenantConfig[] getAllTenants(Start start) throws StorageQueryExcep
try {
// Map TenantIdentifier -> thirdPartyId -> clientType
- HashMap>> providerClientsMap = ThirdPartyProviderClientSQLHelper.selectAll(start);
+ HashMap>> providerClientsMap = ThirdPartyProviderClientSQLHelper.selectAll(
+ start);
// Map (tenantIdentifier) -> thirdPartyId -> provider
- HashMap> providerMap = ThirdPartyProviderSQLHelper.selectAll(start, providerClientsMap);
+ HashMap> providerMap =
+ ThirdPartyProviderSQLHelper.selectAll(
+ start, providerClientsMap);
// Map (tenantIdentifier) -> firstFactors
HashMap firstFactorsMap = MfaSqlHelper.selectAllFirstFactors(start);
// Map (tenantIdentifier) -> requiredSecondaryFactors
- HashMap requiredSecondaryFactorsMap = MfaSqlHelper.selectAllRequiredSecondaryFactors(start);
+ HashMap requiredSecondaryFactorsMap =
+ MfaSqlHelper.selectAllRequiredSecondaryFactors(
+ start);
return TenantConfigSQLHelper.selectAll(start, providerMap, firstFactorsMap, requiredSecondaryFactorsMap);
} catch (SQLException throwables) {
@@ -290,7 +315,7 @@ public static void addTenantIdInTargetStorage(Start start, TenantIdentifier tena
}
public static void addTenantIdInTargetStorage_Transaction(Start start, Connection con,
- TenantIdentifier tenantIdentifier) throws
+ TenantIdentifier tenantIdentifier) throws
SQLException, StorageQueryException {
{
if (Start.isTesting && simulateErrorInAddingTenantIdInTargetStorage_forTesting) {
@@ -314,7 +339,7 @@ public static void addTenantIdInTargetStorage_Transaction(Start start, Connectio
+ "(app_id, created_at_time)" + " VALUES(?, ?) ON CONFLICT DO NOTHING";
update(con, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
- pst.setLong(2, currentTime);
+ pst.setLong(2, currentTime);
});
}
@@ -325,14 +350,14 @@ public static void addTenantIdInTargetStorage_Transaction(Start start, Connectio
update(con, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
pst.setString(2, tenantIdentifier.getTenantId());
- pst.setLong(3, currentTime);
+ pst.setLong(3, currentTime);
});
}
}
}
public static void deleteTenantIdInTargetStorage(Start start, TenantIdentifier tenantIdentifier)
- throws StorageQueryException {
+ throws StorageQueryException {
try {
if (tenantIdentifier.getTenantId().equals(TenantIdentifier.DEFAULT_TENANT_ID)) {
// Delete the app
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java
new file mode 100644
index 00000000..c6f59566
--- /dev/null
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/OAuthQueries.java
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
+ *
+ * This software is licensed under the Apache License, Version 2.0 (the
+ * "License") as published by the Apache Software Foundation.
+ *
+ * You may not use this file except in compliance with the License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.supertokens.storage.postgresql.queries;
+
+import io.supertokens.pluginInterface.exceptions.StorageQueryException;
+import io.supertokens.pluginInterface.multitenancy.AppIdentifier;
+import io.supertokens.pluginInterface.oauth.OAuthClient;
+import io.supertokens.pluginInterface.oauth.OAuthLogoutChallenge;
+import io.supertokens.storage.postgresql.Start;
+import io.supertokens.storage.postgresql.config.Config;
+import io.supertokens.storage.postgresql.utils.Utils;
+import org.jetbrains.annotations.NotNull;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static io.supertokens.storage.postgresql.QueryExecutorTemplate.execute;
+import static io.supertokens.storage.postgresql.QueryExecutorTemplate.update;
+
+
+public class OAuthQueries {
+
+ public static String getQueryToCreateOAuthClientTable(Start start) {
+ String schema = Config.getConfig(start).getTableSchema();
+ String oAuth2ClientTable = Config.getConfig(start).getOAuthClientsTable();
+ // @formatter:off
+ return "CREATE TABLE IF NOT EXISTS " + oAuth2ClientTable + " ("
+ + "app_id VARCHAR(64),"
+ + "client_id VARCHAR(255) NOT NULL,"
+ + "client_secret TEXT,"
+ + "enable_refresh_token_rotation BOOLEAN NOT NULL,"
+ + "is_client_credentials_only BOOLEAN NOT NULL,"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2ClientTable, null, "pkey")
+ + " PRIMARY KEY (app_id, client_id),"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2ClientTable, "app_id", "fkey")
+ + " FOREIGN KEY(app_id) REFERENCES " + Config.getConfig(start).getAppsTable() + "(app_id) ON DELETE CASCADE "
+ + ");";
+ // @formatter:on
+ }
+
+ public static String getQueryToCreateOAuthSessionsTable(Start start) {
+ String schema = Config.getConfig(start).getTableSchema();
+ String oAuthSessionsTable = Config.getConfig(start).getOAuthSessionsTable();
+ // @formatter:off
+ return "CREATE TABLE IF NOT EXISTS " + oAuthSessionsTable + " ("
+ + "gid VARCHAR(255)," // needed for instrospect. It's much easier to find these records if we have a gid
+ + "app_id VARCHAR(64) DEFAULT 'public',"
+ + "client_id VARCHAR(255) NOT NULL,"
+ + "session_handle VARCHAR(128),"
+ + "external_refresh_token VARCHAR(255) UNIQUE,"
+ + "internal_refresh_token VARCHAR(255) UNIQUE,"
+ + "jti TEXT NOT NULL," // comma separated jti list
+ + "exp BIGINT NOT NULL,"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthSessionsTable, null, "pkey")
+ + " PRIMARY KEY (gid),"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuthSessionsTable, "client_id", "fkey")
+ + " FOREIGN KEY(app_id, client_id) REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE);";
+ // @formatter:on
+ }
+
+ public static String getQueryToCreateOAuthSessionsExpIndex(Start start) {
+ String oAuth2SessionTable = Config.getConfig(start).getOAuthSessionsTable();
+ return "CREATE INDEX IF NOT EXISTS oauth_session_exp_index ON "
+ + oAuth2SessionTable + "(exp DESC);";
+ }
+
+ public static String getQueryToCreateOAuthSessionsExternalRefreshTokenIndex(Start start) {
+ String oAuth2SessionTable = Config.getConfig(start).getOAuthSessionsTable();
+ return "CREATE INDEX IF NOT EXISTS oauth_session_external_refresh_token_index ON "
+ + oAuth2SessionTable + "(app_id, external_refresh_token DESC);";
+ }
+
+ public static String getQueryToCreateOAuthM2MTokensTable(Start start) {
+ String schema = Config.getConfig(start).getTableSchema();
+ String oAuth2M2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable();
+ // @formatter:off
+ return "CREATE TABLE IF NOT EXISTS " + oAuth2M2MTokensTable + " ("
+ + "app_id VARCHAR(64) DEFAULT 'public',"
+ + "client_id VARCHAR(255) NOT NULL,"
+ + "iat BIGINT NOT NULL,"
+ + "exp BIGINT NOT NULL,"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2M2MTokensTable, null, "pkey")
+ + " PRIMARY KEY (app_id, client_id, iat),"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2M2MTokensTable, "client_id", "fkey")
+ + " FOREIGN KEY(app_id, client_id)"
+ + " REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE"
+ + ");";
+ // @formatter:on
+ }
+
+ public static String getQueryToCreateOAuthM2MTokenIatIndex(Start start) {
+ String oAuth2M2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable();
+ return "CREATE INDEX IF NOT EXISTS oauth_m2m_token_iat_index ON "
+ + oAuth2M2MTokensTable + "(iat DESC, app_id DESC);";
+ }
+
+ public static String getQueryToCreateOAuthM2MTokenExpIndex(Start start) {
+ String oAuth2M2MTokensTable = Config.getConfig(start).getOAuthM2MTokensTable();
+ return "CREATE INDEX IF NOT EXISTS oauth_m2m_token_exp_index ON "
+ + oAuth2M2MTokensTable + "(exp DESC);";
+ }
+
+ public static String getQueryToCreateOAuthLogoutChallengesTable(Start start) {
+ String schema = Config.getConfig(start).getTableSchema();
+ String oAuth2LogoutChallengesTable = Config.getConfig(start).getOAuthLogoutChallengesTable();
+ // @formatter:off
+ return "CREATE TABLE IF NOT EXISTS " + oAuth2LogoutChallengesTable + " ("
+ + "app_id VARCHAR(64) DEFAULT 'public',"
+ + "challenge VARCHAR(128) NOT NULL,"
+ + "client_id VARCHAR(255) NOT NULL,"
+ + "post_logout_redirect_uri VARCHAR(1024),"
+ + "session_handle VARCHAR(128),"
+ + "state VARCHAR(128),"
+ + "time_created BIGINT NOT NULL,"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2LogoutChallengesTable, null, "pkey")
+ + " PRIMARY KEY (app_id, challenge),"
+ + "CONSTRAINT " + Utils.getConstraintName(schema, oAuth2LogoutChallengesTable, "client_id", "fkey")
+ + " FOREIGN KEY(app_id, client_id)"
+ + " REFERENCES " + Config.getConfig(start).getOAuthClientsTable() + "(app_id, client_id) ON DELETE CASCADE"
+ + ");";
+ // @formatter:on
+ }
+
+ public static String getQueryToCreateOAuthLogoutChallengesTimeCreatedIndex(Start start) {
+ String oAuth2LogoutChallengesTable = Config.getConfig(start).getOAuthLogoutChallengesTable();
+ return "CREATE INDEX IF NOT EXISTS oauth_logout_challenges_time_created_index ON "
+ + oAuth2LogoutChallengesTable + "(time_created DESC);";
+ }
+
+ public static OAuthClient getOAuthClientById(Start start, String clientId, AppIdentifier appIdentifier)
+ throws SQLException, StorageQueryException {
+ String QUERY = "SELECT client_secret, is_client_credentials_only, enable_refresh_token_rotation FROM " + Config.getConfig(start).getOAuthClientsTable() +
+ " WHERE client_id = ? AND app_id = ?";
+
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, clientId);
+ pst.setString(2, appIdentifier.getAppId());
+ }, (result) -> {
+ if (result.next()) {
+ return new OAuthClient(clientId, result.getString("client_secret"), result.getBoolean("is_client_credentials_only"), result.getBoolean("enable_refresh_token_rotation"));
+ }
+ return null;
+ });
+ }
+
+ public static void createOrUpdateOAuthSession(Start start, AppIdentifier appIdentifier, @NotNull String gid, @NotNull String clientId,
+ String externalRefreshToken, String internalRefreshToken, String sessionHandle,
+ String jti, long exp)
+ throws SQLException, StorageQueryException {
+ String sessionTable = Config.getConfig(start).getOAuthSessionsTable();
+ String QUERY = "INSERT INTO " + sessionTable +
+ " (gid, client_id, app_id, external_refresh_token, internal_refresh_token, session_handle, jti, exp) VALUES (?, ?, ?, ?, ?, ?, ?, ?) " +
+ "ON CONFLICT (gid) DO UPDATE SET external_refresh_token = ?, internal_refresh_token = ?, " +
+ "session_handle = ? , jti = CONCAT("+sessionTable+".jti, ?), exp = ?";
+ update(start, QUERY, pst -> {
+ String jtiToInsert = jti + ",";
+
+ pst.setString(1, gid);
+ pst.setString(2, clientId);
+ pst.setString(3, appIdentifier.getAppId());
+ pst.setString(4, externalRefreshToken);
+ pst.setString(5, internalRefreshToken);
+ pst.setString(6, sessionHandle);
+ pst.setString(7, jtiToInsert); //the starting list element also has to have a "," at the end as the remove removes "jti + ,"
+ pst.setLong(8, exp);
+
+ pst.setString(9, externalRefreshToken);
+ pst.setString(10, internalRefreshToken);
+ pst.setString(11, sessionHandle);
+ pst.setString(12, jtiToInsert);
+ pst.setLong(13, exp);
+ });
+ }
+
+ public static List getOAuthClients(Start start, AppIdentifier appIdentifier, List clientIds)
+ throws SQLException, StorageQueryException {
+ if(clientIds.isEmpty()){
+ return Collections.emptyList();
+ }
+ String QUERY = "SELECT * FROM " + Config.getConfig(start).getOAuthClientsTable()
+ + " WHERE app_id = ? AND client_id IN ( "
+ + Utils.generateCommaSeperatedQuestionMarks(clientIds.size())
+ + " );";
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ for (int i = 0; i < clientIds.size(); i++) {
+ pst.setString(i + 2, clientIds.get(i));
+ }
+ }, (result) -> {
+ List res = new ArrayList<>();
+ while (result.next()) {
+ res.add(new OAuthClient(result.getString("client_id"), result.getString("client_secret"), result.getBoolean("is_client_credentials_only"), result.getBoolean("enable_refresh_token_rotation")));
+ }
+ return res;
+ });
+ }
+
+ public static void addOrUpdateOauthClient(Start start, AppIdentifier appIdentifier, String clientId, String clientSecret,
+ boolean isClientCredentialsOnly, boolean enableRefreshTokenRotation)
+ throws SQLException, StorageQueryException {
+ String INSERT = "INSERT INTO " + Config.getConfig(start).getOAuthClientsTable()
+ + "(app_id, client_id, client_secret, is_client_credentials_only, enable_refresh_token_rotation) VALUES(?, ?, ?, ?, ?) "
+ + "ON CONFLICT (app_id, client_id) DO UPDATE SET client_secret = ?, is_client_credentials_only = ?, enable_refresh_token_rotation = ?";
+ update(start, INSERT, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, clientId);
+ pst.setString(3, clientSecret);
+ pst.setBoolean(4, isClientCredentialsOnly);
+ pst.setBoolean(5, enableRefreshTokenRotation);
+ pst.setString(6, clientSecret);
+ pst.setBoolean(7, isClientCredentialsOnly);
+ pst.setBoolean(8, enableRefreshTokenRotation);
+ });
+ }
+
+ public static boolean deleteOAuthClient(Start start, String clientId, AppIdentifier appIdentifier)
+ throws SQLException, StorageQueryException {
+ String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthClientsTable()
+ + " WHERE app_id = ? AND client_id = ?";
+ int numberOfRow = update(start, DELETE, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, clientId);
+ });
+ return numberOfRow > 0;
+ }
+
+ public static boolean deleteOAuthSessionByGID(Start start, AppIdentifier appIdentifier, String gid)
+ throws SQLException, StorageQueryException {
+ String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable()
+ + " WHERE gid = ? and app_id = ?;";
+ int numberOfRows = update(start, DELETE, pst -> {
+ pst.setString(1, gid);
+ pst.setString(2, appIdentifier.getAppId());
+ });
+ return numberOfRows > 0;
+ }
+
+ public static boolean deleteOAuthSessionByClientId(Start start, AppIdentifier appIdentifier, String clientId)
+ throws SQLException, StorageQueryException {
+ String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable()
+ + " WHERE app_id = ? and client_id = ?;";
+ int numberOfRows = update(start, DELETE, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, clientId);
+ });
+ return numberOfRows > 0;
+ }
+
+ public static boolean deleteOAuthSessionBySessionHandle(Start start, AppIdentifier appIdentifier, String sessionHandle)
+ throws SQLException, StorageQueryException {
+ String DELETE = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable()
+ + " WHERE app_id = ? and session_handle = ?";
+ int numberOfRows = update(start, DELETE, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, sessionHandle);
+ });
+ return numberOfRows > 0;
+ }
+
+ public static boolean deleteJTIFromOAuthSession(Start start, AppIdentifier appIdentifier, String gid, String jti)
+ throws SQLException, StorageQueryException {
+ //jti is a comma separated list. When deleting a jti, just have to delete from the list
+ String DELETE = "UPDATE " + Config.getConfig(start).getOAuthSessionsTable()
+ + " SET jti = REPLACE(jti, ?, '')" // deletion means replacing the jti with empty char
+ + " WHERE app_id = ? and gid = ?";
+ int numberOfRows = update(start, DELETE, pst -> {
+ pst.setString(1, jti + ","); //removing with the "," to not leave behind trash
+ pst.setString(2, appIdentifier.getAppId());
+ pst.setString(3, gid);
+ });
+ return numberOfRows > 0;
+ }
+
+ public static int countTotalNumberOfClients(Start start, AppIdentifier appIdentifier,
+ boolean filterByClientCredentialsOnly) throws SQLException, StorageQueryException {
+ if (filterByClientCredentialsOnly) {
+ String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthClientsTable() +
+ " WHERE app_id = ? AND is_client_credentials_only = ?";
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setBoolean(2, true);
+ }, result -> {
+ if (result.next()) {
+ return result.getInt("c");
+ }
+ return 0;
+ });
+ } else {
+ String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthClientsTable() +
+ " WHERE app_id = ?";
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ }, result -> {
+ if (result.next()) {
+ return result.getInt("c");
+ }
+ return 0;
+ });
+ }
+ }
+
+ public static int countTotalNumberOfOAuthM2MTokensAlive(Start start, AppIdentifier appIdentifier)
+ throws SQLException, StorageQueryException {
+ String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthM2MTokensTable() +
+ " WHERE app_id = ? AND exp > ?";
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setLong(2, System.currentTimeMillis()/1000);
+ }, result -> {
+ if (result.next()) {
+ return result.getInt("c");
+ }
+ return 0;
+ });
+ }
+
+ public static int countTotalNumberOfOAuthM2MTokensCreatedSince(Start start, AppIdentifier appIdentifier, long since)
+ throws SQLException, StorageQueryException {
+ String QUERY = "SELECT COUNT(*) as c FROM " + Config.getConfig(start).getOAuthM2MTokensTable() +
+ " WHERE app_id = ? AND iat >= ?";
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setLong(2, since / 1000);
+ }, result -> {
+ if (result.next()) {
+ return result.getInt("c");
+ }
+ return 0;
+ });
+ }
+
+ public static void addOAuthM2MTokenForStats(Start start, AppIdentifier appIdentifier, String clientId, long iat, long exp)
+ throws SQLException, StorageQueryException {
+ String QUERY = "INSERT INTO " + Config.getConfig(start).getOAuthM2MTokensTable() +
+ " (app_id, client_id, iat, exp) VALUES (?, ?, ?, ?) ON CONFLICT DO NOTHING";
+ update(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, clientId);
+ pst.setLong(3, iat);
+ pst.setLong(4, exp);
+ });
+ }
+
+ public static void addOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge, String clientId,
+ String postLogoutRedirectionUri, String sessionHandle, String state, long timeCreated) throws SQLException, StorageQueryException {
+ String QUERY = "INSERT INTO " + Config.getConfig(start).getOAuthLogoutChallengesTable() +
+ " (app_id, challenge, client_id, post_logout_redirect_uri, session_handle, state, time_created) VALUES (?, ?, ?, ?, ?, ?, ?)";
+ update(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, challenge);
+ pst.setString(3, clientId);
+ pst.setString(4, postLogoutRedirectionUri);
+ pst.setString(5, sessionHandle);
+ pst.setString(6, state);
+ pst.setLong(7, timeCreated);
+ });
+ }
+
+ public static OAuthLogoutChallenge getOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge) throws SQLException, StorageQueryException {
+ String QUERY = "SELECT challenge, client_id, post_logout_redirect_uri, session_handle, state, time_created FROM " +
+ Config.getConfig(start).getOAuthLogoutChallengesTable() +
+ " WHERE app_id = ? AND challenge = ?";
+
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, challenge);
+ }, result -> {
+ if (result.next()) {
+ return new OAuthLogoutChallenge(
+ result.getString("challenge"),
+ result.getString("client_id"),
+ result.getString("post_logout_redirect_uri"),
+ result.getString("session_handle"),
+ result.getString("state"),
+ result.getLong("time_created")
+ );
+ }
+ return null;
+ });
+ }
+
+ public static void deleteOAuthLogoutChallenge(Start start, AppIdentifier appIdentifier, String challenge) throws SQLException, StorageQueryException {
+ String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthLogoutChallengesTable() +
+ " WHERE app_id = ? AND challenge = ?";
+ update(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, challenge);
+ });
+ }
+
+ public static void deleteOAuthLogoutChallengesBefore(Start start, long time) throws SQLException, StorageQueryException {
+ String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthLogoutChallengesTable() +
+ " WHERE time_created < ?";
+ update(start, QUERY, pst -> {
+ pst.setLong(1, time);
+ });
+ }
+
+ public static String getRefreshTokenMapping(Start start, AppIdentifier appIdentifier, String externalRefreshToken) throws SQLException, StorageQueryException {
+ String QUERY = "SELECT internal_refresh_token FROM " + Config.getConfig(start).getOAuthSessionsTable() +
+ " WHERE app_id = ? AND external_refresh_token = ?";
+ return execute(start, QUERY, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, externalRefreshToken);
+ }, result -> {
+ if (result.next()) {
+ return result.getString("internal_refresh_token");
+ }
+ return null;
+ });
+ }
+
+ public static void deleteExpiredOAuthSessions(Start start, long exp) throws SQLException, StorageQueryException {
+ // delete expired M2M tokens
+ String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthSessionsTable() +
+ " WHERE exp < ?";
+
+ update(start, QUERY, pst -> {
+ pst.setLong(1, exp);
+ });
+ }
+
+ public static void deleteExpiredOAuthM2MTokens(Start start, long exp) throws SQLException, StorageQueryException {
+ // delete expired M2M tokens
+ String QUERY = "DELETE FROM " + Config.getConfig(start).getOAuthM2MTokensTable() +
+ " WHERE exp < ?";
+ update(start, QUERY, pst -> {
+ pst.setLong(1, exp);
+ });
+ }
+
+ public static boolean isOAuthSessionExistsByJTI(Start start, AppIdentifier appIdentifier, String gid, String jti)
+ throws SQLException, StorageQueryException {
+ String SELECT = "SELECT jti FROM " + Config.getConfig(start).getOAuthSessionsTable()
+ + " WHERE app_id = ? and gid = ?;";
+ return execute(start, SELECT, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, gid);
+ }, result -> {
+ if(result.next()){
+ List jtis = Arrays.stream(result.getString(1).split(",")).filter(s -> !s.isEmpty()).collect(
+ Collectors.toList());
+ return jtis.contains(jti);
+ }
+ return false;
+ });
+ }
+
+ public static boolean isOAuthSessionExistsByGID(Start start, AppIdentifier appIdentifier, String gid)
+ throws SQLException, StorageQueryException {
+ String SELECT = "SELECT count(*) FROM " + Config.getConfig(start).getOAuthSessionsTable()
+ + " WHERE app_id = ? and gid = ?;";
+ return execute(start, SELECT, pst -> {
+ pst.setString(1, appIdentifier.getAppId());
+ pst.setString(2, gid);
+ }, result -> {
+ if(result.next()){
+ return result.getInt(1) > 0;
+ }
+ return false;
+ });
+ }
+
+}
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java
index 8f8df3d6..bfe1a2a9 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/PasswordlessQueries.java
@@ -388,7 +388,8 @@ public static void deleteCode_Transaction(Start start, Connection con, TenantIde
});
}
- public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenantIdentifier, String id, @Nullable String email,
+ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenantIdentifier, String id,
+ @Nullable String email,
@Nullable String phoneNumber, long timeJoined)
throws StorageTransactionLogicException, StorageQueryException {
return start.startTransaction(con -> {
@@ -407,7 +408,9 @@ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenant
{ // all_auth_recipe_users
String QUERY = "INSERT INTO " + getConfig(start).getUsersTable()
- + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" +
+ +
+ "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " +
+ "primary_or_recipe_user_time_joined)" +
" VALUES(?, ?, ?, ?, ?, ?, ?)";
update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
@@ -457,7 +460,7 @@ public static AuthRecipeUserInfo createUser(Start start, TenantIdentifier tenant
}
private static UserInfoWithTenantId[] getUserInfosWithTenant_Transaction(Start start, Connection con,
- AppIdentifier appIdentifier, String userId)
+ AppIdentifier appIdentifier, String userId)
throws StorageQueryException, SQLException {
String QUERY = "SELECT pl_users.user_id as user_id, pl_users.email as email, "
+ "pl_users.phone_number as phone_number, pl_users_to_tenant.tenant_id as tenant_id "
@@ -742,7 +745,7 @@ public static List getUsersInfoUsingIdList(Start start, Set
}
public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids,
- AppIdentifier appIdentifier)
+ AppIdentifier appIdentifier)
throws SQLException, StorageQueryException {
if (ids.size() > 0) {
// No need to filter based on tenantId because the id list is already filtered for a tenant
@@ -772,7 +775,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start,
}
private static UserInfoPartial getUserById_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier,
- String userId)
+ String userId)
throws StorageQueryException, SQLException {
// we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id
// table
@@ -810,8 +813,8 @@ public static List lockEmail_Transaction(Start start, Connection con, Ap
}
public static List lockPhoneAndTenant_Transaction(Start start, Connection con,
- AppIdentifier appIdentifier,
- String phoneNumber)
+ AppIdentifier appIdentifier,
+ String phoneNumber)
throws SQLException, StorageQueryException {
String QUERY = "SELECT user_id FROM " + getConfig(start).getPasswordlessUsersTable() +
@@ -829,7 +832,7 @@ public static List lockPhoneAndTenant_Transaction(Start start, Connectio
}
public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier tenantIdentifier,
- String email)
+ String email)
throws StorageQueryException, SQLException {
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
+ "FROM " + getConfig(start).getPasswordlessUserToTenantTable() + " AS pless" +
@@ -849,8 +852,9 @@ public static String getPrimaryUserIdUsingEmail(Start start, TenantIdentifier te
});
}
- public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con, AppIdentifier appIdentifier,
- String email)
+ public static List getPrimaryUserIdsUsingEmail_Transaction(Start start, Connection con,
+ AppIdentifier appIdentifier,
+ String email)
throws StorageQueryException, SQLException {
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
+ "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" +
@@ -891,8 +895,9 @@ public static String getPrimaryUserByPhoneNumber(Start start, TenantIdentifier t
});
}
- public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con, AppIdentifier appIdentifier,
- @Nonnull String phoneNumber)
+ public static List listUserIdsByPhoneNumber_Transaction(Start start, Connection con,
+ AppIdentifier appIdentifier,
+ @Nonnull String phoneNumber)
throws StorageQueryException, SQLException {
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
+ "FROM " + getConfig(start).getPasswordlessUsersTable() + " AS pless" +
@@ -922,11 +927,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC
throw new UnknownUserIdException();
}
- GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId);
+ GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start,
+ sqlCon, tenantIdentifier.toAppIdentifier(), userId);
{ // all_auth_recipe_users
String QUERY = "INSERT INTO " + getConfig(start).getUsersTable()
- + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)"
+ +
+ "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " +
+ "recipe_id, time_joined, primary_or_recipe_user_time_joined)"
+ " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING";
update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
@@ -939,14 +947,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC
pst.setLong(8, userInfo.timeJoined);
});
- GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), accountLinkingInfo.primaryUserId);
+ GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(),
+ accountLinkingInfo.primaryUserId);
}
{ // passwordless_user_to_tenant
String QUERY = "INSERT INTO " + getConfig(start).getPasswordlessUserToTenantTable()
+ "(app_id, tenant_id, user_id, email, phone_number)"
+ " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT "
- + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getPasswordlessUserToTenantTable(), null, "pkey")
+ + Utils.getConstraintName(Config.getConfig(start).getTableSchema(),
+ getConfig(start).getPasswordlessUserToTenantTable(), null, "pkey")
+ " DO NOTHING";
int numRows = update(sqlCon, QUERY, pst -> {
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java
index 8a8269d5..60270a65 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/TOTPQueries.java
@@ -32,7 +32,7 @@ public static String getQueryToCreateUsersTable(Start start) {
+ " PRIMARY KEY (app_id, user_id),"
+ "CONSTRAINT " + Utils.getConstraintName(schema, tableName, "app_id", "fkey")
+ " FOREIGN KEY(app_id)"
- + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getAppsTable() + " (app_id) ON DELETE CASCADE"
+ ");";
// @formatter:on
}
@@ -119,10 +119,13 @@ private static int insertUser_Transaction(Start start, Connection con, AppIdenti
});
}
- private static int insertDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, TOTPDevice device)
+ private static int insertDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier,
+ TOTPDevice device)
throws SQLException, StorageQueryException {
String QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUserDevicesTable()
- + " (app_id, user_id, device_name, secret_key, period, skew, verified, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
+ +
+ " (app_id, user_id, device_name, secret_key, period, skew, verified, created_at) VALUES (?, ?, ?, ?, " +
+ "?, ?, ?, ?)";
return update(con, QUERY, pst -> {
pst.setString(1, appIdentifier.getAppId());
@@ -136,13 +139,15 @@ private static int insertDevice_Transaction(Start start, Connection con, AppIden
});
}
- public static void createDevice_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, TOTPDevice device)
+ public static void createDevice_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier,
+ TOTPDevice device)
throws SQLException, StorageQueryException {
insertUser_Transaction(start, sqlCon, appIdentifier, device.userId);
insertDevice_Transaction(start, sqlCon, appIdentifier, device);
}
- public static TOTPDevice getDeviceByName_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId, String deviceName)
+ public static TOTPDevice getDeviceByName_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier,
+ String userId, String deviceName)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getTotpUserDevicesTable()
+ " WHERE app_id = ? AND user_id = ? AND device_name = ? FOR UPDATE;";
@@ -170,7 +175,8 @@ public static int markDeviceAsVerified(Start start, AppIdentifier appIdentifier,
});
}
- public static int deleteDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId, String deviceName)
+ public static int deleteDevice_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId,
+ String deviceName)
throws SQLException, StorageQueryException {
String QUERY = "DELETE FROM " + Config.getConfig(start).getTotpUserDevicesTable()
+ " WHERE app_id = ? AND user_id = ? AND device_name = ?;";
@@ -207,7 +213,8 @@ public static boolean removeUser(Start start, TenantIdentifier tenantIdentifier,
return removedUsersCount > 0;
}
- public static int updateDeviceName(Start start, AppIdentifier appIdentifier, String userId, String oldDeviceName, String newDeviceName)
+ public static int updateDeviceName(Start start, AppIdentifier appIdentifier, String userId, String oldDeviceName,
+ String newDeviceName)
throws StorageQueryException, SQLException {
String QUERY = "UPDATE " + Config.getConfig(start).getTotpUserDevicesTable()
+ " SET device_name = ? WHERE app_id = ? AND user_id = ? AND device_name = ?;";
@@ -238,7 +245,8 @@ public static TOTPDevice[] getDevices(Start start, AppIdentifier appIdentifier,
});
}
- public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, AppIdentifier appIdentifier, String userId)
+ public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, AppIdentifier appIdentifier,
+ String userId)
throws StorageQueryException, SQLException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getTotpUserDevicesTable()
+ " WHERE app_id = ? AND user_id = ? FOR UPDATE;";
@@ -257,10 +265,13 @@ public static TOTPDevice[] getDevices_Transaction(Start start, Connection con, A
}
- public static int insertUsedCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier, TOTPUsedCode code)
+ public static int insertUsedCode_Transaction(Start start, Connection con, TenantIdentifier tenantIdentifier,
+ TOTPUsedCode code)
throws SQLException, StorageQueryException {
String QUERY = "INSERT INTO " + Config.getConfig(start).getTotpUsedCodesTable()
- + " (app_id, tenant_id, user_id, code, is_valid, expiry_time_ms, created_time_ms) VALUES (?, ?, ?, ?, ?, ?, ?);";
+ +
+ " (app_id, tenant_id, user_id, code, is_valid, expiry_time_ms, created_time_ms) VALUES (?, ?, ?, ?, " +
+ "?, ?, ?);";
return update(con, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
@@ -278,7 +289,7 @@ public static int insertUsedCode_Transaction(Start start, Connection con, Tenant
* order of creation time.
*/
public static TOTPUsedCode[] getAllUsedCodesDescOrder_Transaction(Start start, Connection con,
- TenantIdentifier tenantIdentifier, String userId)
+ TenantIdentifier tenantIdentifier, String userId)
throws SQLException, StorageQueryException {
// Take a lock based on the user id:
String QUERY = "SELECT * FROM " +
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java
index 2a37c9dc..964b53cd 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/ThirdPartyQueries.java
@@ -117,7 +117,9 @@ public static AuthRecipeUserInfo signUp(Start start, TenantIdentifier tenantIden
{ // all_auth_recipe_users
String QUERY = "INSERT INTO " + getConfig(start).getUsersTable()
- + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, primary_or_recipe_user_time_joined)" +
+ +
+ "(app_id, tenant_id, user_id, primary_or_recipe_user_id, recipe_id, time_joined, " +
+ "primary_or_recipe_user_time_joined)" +
" VALUES(?, ?, ?, ?, ?, ?, ?)";
update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
@@ -274,7 +276,7 @@ public static List getUsersInfoUsingIdList(Start start, Set
}
public static List getUsersInfoUsingIdList_Transaction(Start start, Connection con, Set ids,
- AppIdentifier appIdentifier)
+ AppIdentifier appIdentifier)
throws SQLException, StorageQueryException {
if (ids.size() > 0) {
String QUERY = "SELECT user_id, third_party_id, third_party_user_id, email, time_joined "
@@ -305,7 +307,7 @@ public static List getUsersInfoUsingIdList_Transaction(Start start,
public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifier appIdentifier,
- String thirdPartyId, String thirdPartyUserId)
+ String thirdPartyId, String thirdPartyUserId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT DISTINCT all_users.primary_or_recipe_user_id AS user_id "
@@ -327,7 +329,8 @@ public static List listUserIdsByThirdPartyInfo(Start start, AppIdentifie
});
}
- public static List listUserIdsByThirdPartyInfo_Transaction(Start start, Connection con, AppIdentifier appIdentifier,
+ public static List listUserIdsByThirdPartyInfo_Transaction(Start start, Connection con,
+ AppIdentifier appIdentifier,
String thirdPartyId, String thirdPartyUserId)
throws SQLException, StorageQueryException {
@@ -388,7 +391,7 @@ public static void updateUserEmail_Transaction(Start start, Connection con, AppI
}
private static UserInfoPartial getUserInfoUsingUserId_Transaction(Start start, Connection con,
- AppIdentifier appIdentifier, String userId)
+ AppIdentifier appIdentifier, String userId)
throws SQLException, StorageQueryException {
// we don't need a LOCK here because this is already part of a transaction, and locked on app_id_to_user_id
@@ -462,11 +465,14 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC
throw new UnknownUserIdException();
}
- GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), userId);
+ GeneralQueries.AccountLinkingInfo accountLinkingInfo = GeneralQueries.getAccountLinkingInfo_Transaction(start,
+ sqlCon, tenantIdentifier.toAppIdentifier(), userId);
{ // all_auth_recipe_users
String QUERY = "INSERT INTO " + getConfig(start).getUsersTable()
- + "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, recipe_id, time_joined, primary_or_recipe_user_time_joined)"
+ +
+ "(app_id, tenant_id, user_id, primary_or_recipe_user_id, is_linked_or_is_a_primary_user, " +
+ "recipe_id, time_joined, primary_or_recipe_user_time_joined)"
+ " VALUES(?, ?, ?, ?, ?, ?, ?, ?)" + " ON CONFLICT DO NOTHING";
update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
@@ -479,14 +485,16 @@ public static boolean addUserIdToTenant_Transaction(Start start, Connection sqlC
pst.setLong(8, userInfo.timeJoined);
});
- GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(), accountLinkingInfo.primaryUserId);
+ GeneralQueries.updateTimeJoinedForPrimaryUser_Transaction(start, sqlCon, tenantIdentifier.toAppIdentifier(),
+ accountLinkingInfo.primaryUserId);
}
{ // thirdparty_user_to_tenant
String QUERY = "INSERT INTO " + getConfig(start).getThirdPartyUserToTenantTable()
+ "(app_id, tenant_id, user_id, third_party_id, third_party_user_id)"
+ " VALUES(?, ?, ?, ?, ?)" + " ON CONFLICT ON CONSTRAINT "
- + Utils.getConstraintName(Config.getConfig(start).getTableSchema(), getConfig(start).getThirdPartyUserToTenantTable(), null, "pkey")
+ + Utils.getConstraintName(Config.getConfig(start).getTableSchema(),
+ getConfig(start).getThirdPartyUserToTenantTable(), null, "pkey")
+ " DO NOTHING";
int numRows = update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getAppId());
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java
index a2388765..072571c1 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserIdMappingQueries.java
@@ -55,7 +55,8 @@ public static String getQueryToCreateUserIdMappingTable(Start start) {
+ " PRIMARY KEY(app_id, supertokens_user_id, external_user_id),"
+ "CONSTRAINT " + Utils.getConstraintName(schema, userIdMappingTable, "supertokens_user_id", "fkey")
+ " FOREIGN KEY (app_id, supertokens_user_id)"
- + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id, user_id)" + " ON DELETE CASCADE"
+ + " REFERENCES " + Config.getConfig(start).getAppIdToUserIdTable() + "(app_id, user_id)" +
+ " ON DELETE CASCADE"
+ ");";
// @formatter:on
}
@@ -65,7 +66,8 @@ public static String getQueryToCreateSupertokensUserIdIndexForUserIdMappingTable
+ getConfig(start).getUserIdMappingTable() + "(app_id, supertokens_user_id);";
}
- public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, String superTokensUserId, String externalUserId,
+ public static void createUserIdMapping(Start start, AppIdentifier appIdentifier, String superTokensUserId,
+ String externalUserId,
String externalUserIdInfo) throws SQLException, StorageQueryException {
String QUERY = "INSERT INTO " + Config.getConfig(start).getUserIdMappingTable()
+ " (app_id, supertokens_user_id, external_user_id, external_user_id_info)" + " VALUES(?, ?, ?, ?)";
@@ -78,7 +80,8 @@ public static void createUserIdMapping(Start start, AppIdentifier appIdentifier,
});
}
- public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId)
+ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable()
+ " WHERE app_id = ? AND supertokens_user_id = ?";
@@ -93,7 +96,8 @@ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId(Start start,
});
}
- public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId)
+ public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable()
+ " WHERE app_id = ? AND external_user_id = ?";
@@ -110,7 +114,9 @@ public static UserIdMapping getUserIdMappingWithExternalUserId(Start start, AppI
}
public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId(Start start,
- AppIdentifier appIdentifier, String userId) throws SQLException, StorageQueryException {
+ AppIdentifier appIdentifier,
+ String userId)
+ throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable()
+ " WHERE app_id = ? AND (supertokens_user_id = ? OR external_user_id = ?)";
@@ -201,7 +207,8 @@ public static HashMap getUserIdMappingWithUserIds_Transaction(St
});
}
- public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier, String userId)
+ public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable()
+ " WHERE app_id = ? AND supertokens_user_id = ?";
@@ -217,7 +224,8 @@ public static boolean deleteUserIdMappingWithSuperTokensUserId(Start start, AppI
public static boolean deleteUserIdMappingWithExternalUserId(Start start, AppIdentifier appIdentifier, String userId)
throws SQLException, StorageQueryException {
- String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() + " WHERE app_id = ? AND external_user_id = ?";
+ String QUERY = "DELETE FROM " + Config.getConfig(start).getUserIdMappingTable() +
+ " WHERE app_id = ? AND external_user_id = ?";
// store the number of rows updated
int rowUpdatedCount = update(start, QUERY, pst -> {
@@ -229,8 +237,10 @@ public static boolean deleteUserIdMappingWithExternalUserId(Start start, AppIden
}
public static boolean updateOrDeleteExternalUserIdInfoWithSuperTokensUserId(Start start,
- AppIdentifier appIdentifier, String userId,
- @Nullable String externalUserIdInfo) throws SQLException, StorageQueryException {
+ AppIdentifier appIdentifier,
+ String userId,
+ @Nullable String externalUserIdInfo)
+ throws SQLException, StorageQueryException {
String QUERY = "UPDATE " + Config.getConfig(start).getUserIdMappingTable()
+ " SET external_user_id_info = ? WHERE app_id = ? AND supertokens_user_id = ?";
@@ -245,7 +255,8 @@ public static boolean updateOrDeleteExternalUserIdInfoWithSuperTokensUserId(Star
public static boolean updateOrDeleteExternalUserIdInfoWithExternalUserId(Start start, AppIdentifier appIdentifier,
String userId,
- @Nullable String externalUserIdInfo) throws SQLException, StorageQueryException {
+ @Nullable String externalUserIdInfo)
+ throws SQLException, StorageQueryException {
String QUERY = "UPDATE " + Config.getConfig(start).getUserIdMappingTable()
+ " SET external_user_id_info = ? WHERE app_id = ? AND external_user_id = ?";
@@ -258,7 +269,9 @@ public static boolean updateOrDeleteExternalUserIdInfoWithExternalUserId(Start s
return rowUpdated > 0;
}
- public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId)
+ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(Start start, Connection sqlCon,
+ AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable()
+ " WHERE app_id = ? AND supertokens_user_id = ?";
@@ -273,7 +286,9 @@ public static UserIdMapping getuseraIdMappingWithSuperTokensUserId_Transaction(S
});
}
- public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start start, Connection sqlCon, AppIdentifier appIdentifier, String userId)
+ public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start start, Connection sqlCon,
+ AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable()
+ " WHERE app_id = ? AND external_user_id = ?";
@@ -289,8 +304,10 @@ public static UserIdMapping getUserIdMappingWithExternalUserId_Transaction(Start
});
}
- public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(Start start, Connection sqlCon,
- AppIdentifier appIdentifier, String userId)
+ public static UserIdMapping[] getUserIdMappingWithEitherSuperTokensUserIdOrExternalUserId_Transaction(Start start,
+ Connection sqlCon,
+ AppIdentifier appIdentifier,
+ String userId)
throws SQLException, StorageQueryException {
String QUERY = "SELECT * FROM " + Config.getConfig(start).getUserIdMappingTable()
+ " WHERE app_id = ? AND (supertokens_user_id = ? OR external_user_id = ?)";
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java
index 10fcb1a7..5825164c 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/UserRolesQueries.java
@@ -142,7 +142,7 @@ public static void addPermissionToRoleOrDoNothingIfExists_Transaction(Start star
}
public static boolean deleteRole(Start start, AppIdentifier appIdentifier,
- String role)
+ String role)
throws SQLException, StorageQueryException {
String QUERY = "DELETE FROM " + getConfig(start).getRolesTable()
+ " WHERE app_id = ? AND role = ? ;";
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java
index b5abf91d..ccbab4f6 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/MfaSqlHelper.java
@@ -33,11 +33,13 @@ public static HashMap selectAllFirstFactors(Start st
throws SQLException, StorageQueryException {
String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, factor_id FROM "
+ getConfig(start).getTenantFirstFactorsTable() + ";";
- return execute(start, QUERY, pst -> {}, result -> {
+ return execute(start, QUERY, pst -> {
+ }, result -> {
HashMap> firstFactors = new HashMap<>();
while (result.next()) {
- TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id"));
+ TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"),
+ result.getString("app_id"), result.getString("tenant_id"));
if (!firstFactors.containsKey(tenantIdentifier)) {
firstFactors.put(tenantIdentifier, new ArrayList<>());
}
@@ -58,7 +60,8 @@ public static HashMap selectAllRequiredSecondaryFact
throws SQLException, StorageQueryException {
String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, factor_id FROM "
+ getConfig(start).getTenantRequiredSecondaryFactorsTable() + ";";
- return execute(start, QUERY, pst -> {}, result -> {
+ return execute(start, QUERY, pst -> {
+ }, result -> {
HashMap> defaultRequiredFactors = new HashMap<>();
while (result.next()) {
@@ -80,16 +83,18 @@ public static HashMap selectAllRequiredSecondaryFact
});
}
- public static void createFirstFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String[] firstFactors)
+ public static void createFirstFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier,
+ String[] firstFactors)
throws SQLException, StorageQueryException {
if (firstFactors == null || firstFactors.length == 0) {
return;
}
- String QUERY = "INSERT INTO " + getConfig(start).getTenantFirstFactorsTable() + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);";
+ String QUERY = "INSERT INTO " + getConfig(start).getTenantFirstFactorsTable() +
+ "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);";
for (String factorId : new HashSet<>(Arrays.asList(firstFactors))) {
update(sqlCon, QUERY, pst -> {
- pst.setString(1, tenantIdentifier.getConnectionUriDomain());
+ pst.setString(1, tenantIdentifier.getConnectionUriDomain());
pst.setString(2, tenantIdentifier.getAppId());
pst.setString(3, tenantIdentifier.getTenantId());
pst.setString(4, factorId);
@@ -97,13 +102,15 @@ public static void createFirstFactors(Start start, Connection sqlCon, TenantIden
}
}
- public static void createRequiredSecondaryFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier, String[] requiredSecondaryFactors)
+ public static void createRequiredSecondaryFactors(Start start, Connection sqlCon, TenantIdentifier tenantIdentifier,
+ String[] requiredSecondaryFactors)
throws SQLException, StorageQueryException {
if (requiredSecondaryFactors == null || requiredSecondaryFactors.length == 0) {
return;
}
- String QUERY = "INSERT INTO " + getConfig(start).getTenantRequiredSecondaryFactorsTable() + "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);";
+ String QUERY = "INSERT INTO " + getConfig(start).getTenantRequiredSecondaryFactorsTable() +
+ "(connection_uri_domain, app_id, tenant_id, factor_id) VALUES (?, ?, ?, ?);";
for (String factorId : requiredSecondaryFactors) {
update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantIdentifier.getConnectionUriDomain());
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java
index 1a2e00b2..79b4fb18 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/TenantConfigSQLHelper.java
@@ -16,15 +16,11 @@
package io.supertokens.storage.postgresql.queries.multitenancy;
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.JsonArray;
import io.supertokens.pluginInterface.RowMapper;
import io.supertokens.pluginInterface.exceptions.StorageQueryException;
import io.supertokens.pluginInterface.multitenancy.*;
import io.supertokens.storage.postgresql.Start;
import io.supertokens.storage.postgresql.queries.utils.JsonUtils;
-import io.supertokens.storage.postgresql.utils.Utils;
import java.sql.Connection;
import java.sql.ResultSet;
@@ -43,25 +39,32 @@ public static class TenantConfigRowMapper implements RowMapper> providerMap, HashMap firstFactorsMap, HashMap requiredSecondaryFactorsMap)
+ public static TenantConfig[] selectAll(Start start,
+ HashMap> providerMap,
+ HashMap firstFactorsMap,
+ HashMap requiredSecondaryFactorsMap)
throws SQLException, StorageQueryException {
String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, core_config,"
- + " email_password_enabled, passwordless_enabled, third_party_enabled FROM "
+ + " email_password_enabled, passwordless_enabled, third_party_enabled, "
+ + " is_first_factors_null FROM "
+ getConfig(start).getTenantConfigsTable() + ";";
- TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> {}, result -> {
+ TenantConfig[] tenantConfigs = execute(start, QUERY, pst -> {
+ }, result -> {
List temp = new ArrayList<>();
while (result.next()) {
- TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id"));
- ThirdPartyConfig.Provider[] providers = null;
+ TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"),
+ result.getString("app_id"), result.getString("tenant_id"));
+ ThirdPartyConfig.Provider[] providers;
if (providerMap.containsKey(tenantIdentifier)) {
providers = providerMap.get(tenantIdentifier).values().toArray(new ThirdPartyConfig.Provider[0]);
+ } else {
+ providers = new ThirdPartyConfig.Provider[0];
}
- String[] firstFactors = firstFactorsMap.containsKey(tenantIdentifier) ? firstFactorsMap.get(tenantIdentifier) : new String[0];
+ String[] firstFactors =
+ firstFactorsMap.containsKey(tenantIdentifier) ? firstFactorsMap.get(tenantIdentifier) :
+ new String[0];
- String[] requiredSecondaryFactors = requiredSecondaryFactorsMap.containsKey(tenantIdentifier) ? requiredSecondaryFactorsMap.get(tenantIdentifier) : new String[0];
+ String[] requiredSecondaryFactors = requiredSecondaryFactorsMap.containsKey(tenantIdentifier) ?
+ requiredSecondaryFactorsMap.get(tenantIdentifier) : new String[0];
- temp.add(TenantConfigSQLHelper.TenantConfigRowMapper.getInstance(providers, firstFactors, requiredSecondaryFactors).mapOrThrow(result));
+ temp.add(TenantConfigSQLHelper.TenantConfigRowMapper.getInstance(providers, firstFactors,
+ requiredSecondaryFactors).mapOrThrow(result));
}
TenantConfig[] finalResult = new TenantConfig[temp.size()];
for (int i = 0; i < temp.size(); i++) {
@@ -104,8 +119,9 @@ public static void create(Start start, Connection sqlCon, TenantConfig tenantCon
throws SQLException, StorageQueryException {
String QUERY = "INSERT INTO " + getConfig(start).getTenantConfigsTable()
+ "(connection_uri_domain, app_id, tenant_id, core_config,"
- + " email_password_enabled, passwordless_enabled, third_party_enabled)"
- + " VALUES(?, ?, ?, ?, ?, ?, ?)";
+ + " email_password_enabled, passwordless_enabled, third_party_enabled,"
+ + " is_first_factors_null)"
+ + " VALUES(?, ?, ?, ?, ?, ?, ?, ?)";
update(sqlCon, QUERY, pst -> {
pst.setString(1, tenantConfig.tenantIdentifier.getConnectionUriDomain());
@@ -115,6 +131,7 @@ public static void create(Start start, Connection sqlCon, TenantConfig tenantCon
pst.setBoolean(5, tenantConfig.emailPasswordConfig.enabled);
pst.setBoolean(6, tenantConfig.passwordlessConfig.enabled);
pst.setBoolean(7, tenantConfig.thirdPartyConfig.enabled);
+ pst.setBoolean(8, tenantConfig.firstFactors == null);
});
}
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java
index ced73d6e..100194ea 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderClientSQLHelper.java
@@ -35,7 +35,8 @@
public class ThirdPartyProviderClientSQLHelper {
public static class TenantThirdPartyProviderClientRowMapper implements
RowMapper {
- public static final ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper INSTANCE = new ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper();
+ public static final ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper INSTANCE =
+ new ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper();
private TenantThirdPartyProviderClientRowMapper() {
}
@@ -77,37 +78,48 @@ public ThirdPartyConfig.ProviderClient map(ResultSet result) throws StorageQuery
}
}
- public static HashMap>> selectAll(Start start)
+ public static HashMap>> selectAll(
+ Start start)
throws SQLException, StorageQueryException {
HashMap>> providerClientsMap = new HashMap<>();
- String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, scope, force_pkce, additional_config FROM "
- + getConfig(start).getTenantThirdPartyProviderClientsTable() + ";";
+ String QUERY =
+ "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, " +
+ "client_secret, scope, force_pkce, additional_config FROM "
+ + getConfig(start).getTenantThirdPartyProviderClientsTable() + ";";
- execute(start, QUERY, pst -> {}, result -> {
+ execute(start, QUERY, pst -> {
+ }, result -> {
while (result.next()) {
- TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id"));
- ThirdPartyConfig.ProviderClient providerClient = ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper.getInstance().mapOrThrow(result);
+ TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"),
+ result.getString("app_id"), result.getString("tenant_id"));
+ ThirdPartyConfig.ProviderClient providerClient =
+ ThirdPartyProviderClientSQLHelper.TenantThirdPartyProviderClientRowMapper.getInstance()
+ .mapOrThrow(result);
if (!providerClientsMap.containsKey(tenantIdentifier)) {
providerClientsMap.put(tenantIdentifier, new HashMap<>());
}
- if(!providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) {
+ if (!providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) {
providerClientsMap.get(tenantIdentifier).put(result.getString("third_party_id"), new HashMap<>());
}
- providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).put(providerClient.clientType, providerClient);
+ providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id"))
+ .put(providerClient.clientType, providerClient);
}
return null;
});
return providerClientsMap;
}
- public static void create(Start start, Connection sqlCon, TenantConfig tenantConfig, ThirdPartyConfig.Provider provider, ThirdPartyConfig.ProviderClient providerClient)
+ public static void create(Start start, Connection sqlCon, TenantConfig tenantConfig,
+ ThirdPartyConfig.Provider provider, ThirdPartyConfig.ProviderClient providerClient)
throws SQLException, StorageQueryException {
String QUERY = "INSERT INTO " + getConfig(start).getTenantThirdPartyProviderClientsTable()
- + "(connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, scope, force_pkce, additional_config)"
+ +
+ "(connection_uri_domain, app_id, tenant_id, third_party_id, client_type, client_id, client_secret, " +
+ "scope, force_pkce, additional_config)"
+ " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
Array scopeArray;
diff --git a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java
index 3f2b6645..321f80d5 100644
--- a/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java
+++ b/src/main/java/io/supertokens/storage/postgresql/queries/multitenancy/ThirdPartyProviderSQLHelper.java
@@ -40,7 +40,8 @@ private TenantThirdPartyProviderRowMapper(ThirdPartyConfig.ProviderClient[] clie
this.clients = clients;
}
- public static ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper getInstance(ThirdPartyConfig.ProviderClient[] clients) {
+ public static ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper getInstance(
+ ThirdPartyConfig.ProviderClient[] clients) {
return new ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper(clients);
}
@@ -82,21 +83,36 @@ public ThirdPartyConfig.Provider map(ResultSet result) throws StorageQueryExcept
}
}
- public static HashMap> selectAll(Start start, HashMap>> providerClientsMap)
+ public static HashMap> selectAll(Start start,
+ HashMap>> providerClientsMap)
throws SQLException, StorageQueryException {
HashMap> providerMap = new HashMap<>();
- String QUERY = "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, name, authorization_endpoint, authorization_endpoint_query_params, token_endpoint, token_endpoint_body_params, user_info_endpoint, user_info_endpoint_query_params, user_info_endpoint_headers, jwks_uri, oidc_discovery_endpoint, require_email, user_info_map_from_id_token_payload_user_id, user_info_map_from_id_token_payload_email, user_info_map_from_id_token_payload_email_verified, user_info_map_from_user_info_endpoint_user_id, user_info_map_from_user_info_endpoint_email, user_info_map_from_user_info_endpoint_email_verified FROM "
- + getConfig(start).getTenantThirdPartyProvidersTable() + ";";
+ String QUERY =
+ "SELECT connection_uri_domain, app_id, tenant_id, third_party_id, name, authorization_endpoint, " +
+ "authorization_endpoint_query_params, token_endpoint, token_endpoint_body_params, " +
+ "user_info_endpoint, user_info_endpoint_query_params, user_info_endpoint_headers, jwks_uri, " +
+ "oidc_discovery_endpoint, require_email, user_info_map_from_id_token_payload_user_id, " +
+ "user_info_map_from_id_token_payload_email, " +
+ "user_info_map_from_id_token_payload_email_verified, " +
+ "user_info_map_from_user_info_endpoint_user_id, user_info_map_from_user_info_endpoint_email, " +
+ "user_info_map_from_user_info_endpoint_email_verified FROM "
+ + getConfig(start).getTenantThirdPartyProvidersTable() + ";";
- execute(start, QUERY, pst -> {}, result -> {
+ execute(start, QUERY, pst -> {
+ }, result -> {
while (result.next()) {
- TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"), result.getString("app_id"), result.getString("tenant_id"));
+ TenantIdentifier tenantIdentifier = new TenantIdentifier(result.getString("connection_uri_domain"),
+ result.getString("app_id"), result.getString("tenant_id"));
ThirdPartyConfig.ProviderClient[] clients = null;
- if (providerClientsMap.containsKey(tenantIdentifier) && providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) {
- clients = providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).values().toArray(new ThirdPartyConfig.ProviderClient[0]);
+ if (providerClientsMap.containsKey(tenantIdentifier) &&
+ providerClientsMap.get(tenantIdentifier).containsKey(result.getString("third_party_id"))) {
+ clients = providerClientsMap.get(tenantIdentifier).get(result.getString("third_party_id")).values()
+ .toArray(new ThirdPartyConfig.ProviderClient[0]);
}
- ThirdPartyConfig.Provider provider = ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper.getInstance(clients).mapOrThrow(result);
+ ThirdPartyConfig.Provider provider =
+ ThirdPartyProviderSQLHelper.TenantThirdPartyProviderRowMapper.getInstance(
+ clients).mapOrThrow(result);
if (!providerMap.containsKey(tenantIdentifier)) {
providerMap.put(tenantIdentifier, new HashMap<>());
@@ -108,10 +124,19 @@ public static HashMap {
pst.setString(1, tenantConfig.tenantIdentifier.getConnectionUriDomain());
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java b/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java
index 6891f739..755b6720 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/ConfigTest.java
@@ -662,9 +662,12 @@ public void testAllConfigsHaveAnAnnotation() throws Exception {
continue;
}
- if (!(field.isAnnotationPresent(UserPoolProperty.class) || field.isAnnotationPresent(ConnectionPoolProperty.class) || field.isAnnotationPresent(
+ if (!(field.isAnnotationPresent(UserPoolProperty.class) ||
+ field.isAnnotationPresent(ConnectionPoolProperty.class) || field.isAnnotationPresent(
NotConflictingWithinUserPool.class))) {
- fail(field.getName() + " does not have UserPoolProperty, ConnectionPoolProperty or NotConflictingWithinUserPool annotation");
+ fail(field.getName() +
+ " does not have UserPoolProperty, ConnectionPoolProperty or NotConflictingWithinUserPool " +
+ "annotation");
}
}
}
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java
index 470e6ce0..3a7f5935 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/DbConnectionPoolTest.java
@@ -154,14 +154,15 @@ public void testDownTimeWhenChangingConnectionPoolSize() throws Exception {
try {
TenantIdentifier t1 = new TenantIdentifier(null, null, "t1");
Storage t1Storage = (StorageLayer.getStorage(t1, process.getProcess()));
- ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid"+ finalI, "user" +
+ ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid" + finalI, "user" +
finalI + "@example.com");
if (firstErrorTime.get() != -1 && successAfterErrorTime.get() == -1) {
successAfterErrorTime.set(System.currentTimeMillis());
}
} catch (StorageQueryException e) {
- if (e.getMessage().contains("Connection is closed") || e.getMessage().contains("has been closed")) {
+ if (e.getMessage().contains("Connection is closed") ||
+ e.getMessage().contains("has been closed")) {
if (firstErrorTime.get() == -1) {
firstErrorTime.set(System.currentTimeMillis());
}
@@ -286,6 +287,16 @@ public void testMinimumIdleConnectionForTenants() throws Exception {
Thread.sleep(1000); // let the new tenant be ready
+ for (int retry = 0; retry < 5; retry++) {
+ try {
+ assertEquals(10, start.getDbActivityCount("st1"));
+ break;
+ } catch (AssertionError e) {
+ Thread.sleep(1000);
+ continue;
+ }
+ }
+
assertEquals(10, start.getDbActivityCount("st1"));
// change connection pool size
@@ -355,7 +366,7 @@ public void testIdleConnectionTimeout() throws Exception {
try {
TenantIdentifier t1 = new TenantIdentifier(null, null, "t1");
Storage t1Storage = (StorageLayer.getStorage(t1, process.getProcess()));
- ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid"+ finalI, "user" +
+ ThirdParty.signInUp(t1, t1Storage, process.getProcess(), "google", "googleid" + finalI, "user" +
finalI + "@example.com");
} catch (StorageQueryException e) {
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java
index 22fe29bb..f86faadf 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/DeadlockTest.java
@@ -263,9 +263,11 @@ public void testCodeCreationRapidlyWithDifferentEmails() throws Exception {
System.out.println("Durations: " + durations.toString());
assertNull(process
- .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
+ .checkOrWaitForEventInPlugin(
+ io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
assertNotNull(process
- .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
+ .checkOrWaitForEventInPlugin(
+ io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
assert (pass.get());
@@ -627,7 +629,8 @@ public void testLinkAccountsInParallel() throws Exception {
for (int i = 0; i < 3000; i++) {
es.execute(() -> {
try {
- AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(), user1.getSupertokensUserId());
+ AuthRecipe.linkAccounts(process.getProcess(), user2.getSupertokensUserId(),
+ user1.getSupertokensUserId());
AuthRecipe.unlinkAccounts(process.getProcess(), user2.getSupertokensUserId());
} catch (Exception e) {
if (e.getMessage().toLowerCase().contains("the transaction might succeed if retried")) {
@@ -642,9 +645,11 @@ public void testLinkAccountsInParallel() throws Exception {
assert (pass.get());
assertNull(process
- .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
+ .checkOrWaitForEventInPlugin(
+ io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
assertNotNull(process
- .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
+ .checkOrWaitForEventInPlugin(
+ io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
@@ -685,9 +690,11 @@ public void testCreatePrimaryInParallel() throws Exception {
assert (pass.get());
assertNull(process
- .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
+ .checkOrWaitForEventInPlugin(
+ io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_NOT_RESOLVED));
assertNotNull(process
- .checkOrWaitForEventInPlugin(io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
+ .checkOrWaitForEventInPlugin(
+ io.supertokens.storage.postgresql.ProcessState.PROCESS_STATE.DEADLOCK_FOUND));
process.kill();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java b/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java
index 996ed4d2..e675c765 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/LogLevelTest.java
@@ -52,7 +52,7 @@ public void beforeEach() {
public void testLogLevelNoneOutput() throws Exception {
{
Utils.setValueInConfig("log_level", "NONE");
- String[] args = { "../" };
+ String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
@@ -96,7 +96,7 @@ public void testLogLevelNoneOutput() throws Exception {
public void testLogLevelErrorOutput() throws Exception {
{
Utils.setValueInConfig("log_level", "ERROR");
- String[] args = { "../" };
+ String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
@@ -150,7 +150,7 @@ public void testLogLevelErrorOutput() throws Exception {
public void testLogLevelWarnOutput() throws Exception {
{
Utils.setValueInConfig("log_level", "WARN");
- String[] args = { "../" };
+ String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
@@ -204,7 +204,7 @@ public void testLogLevelWarnOutput() throws Exception {
public void testLogLevelInfoOutput() throws Exception {
{
Utils.setValueInConfig("log_level", "INFO");
- String[] args = { "../" };
+ String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
@@ -258,7 +258,7 @@ public void testLogLevelInfoOutput() throws Exception {
public void testLogLevelDebugOutput() throws Exception {
{
Utils.setValueInConfig("log_level", "DEBUG");
- String[] args = { "../" };
+ String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java
index 591a4ac0..52602c2c 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/LoggingTest.java
@@ -44,7 +44,6 @@
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
-import java.util.List;
import java.util.Scanner;
import static org.junit.Assert.*;
@@ -65,7 +64,7 @@ public void beforeEach() {
@Test
public void defaultLogging() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
StorageLayer.close();
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
@@ -109,7 +108,7 @@ public void defaultLogging() throws Exception {
@Test
public void customLogging() throws Exception {
try {
- String[] args = { "../" };
+ String[] args = {"../"};
Utils.setValueInConfig("info_log_path", "\"tempLogging/info.log\"");
Utils.setValueInConfig("error_log_path", "\"tempLogging/error.log\"");
@@ -160,7 +159,7 @@ public void customLogging() throws Exception {
@Test
public void confirmLoggerClosed() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
StorageLayer.close();
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
@@ -188,7 +187,7 @@ public void confirmLoggerClosed() throws Exception {
@Test
public void testStandardOutLoggingWithNullStr() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
ByteArrayOutputStream stdOutput = new ByteArrayOutputStream();
ByteArrayOutputStream errorOutput = new ByteArrayOutputStream();
@@ -224,7 +223,7 @@ public void testStandardOutLoggingWithNullStr() throws Exception {
@Test
public void testStandardOutLoggingWithNull() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
ByteArrayOutputStream stdOutput = new ByteArrayOutputStream();
ByteArrayOutputStream errorOutput = new ByteArrayOutputStream();
@@ -261,7 +260,7 @@ public void testStandardOutLoggingWithNull() throws Exception {
@Test
public void confirmHikariLoggerClosedOnlyWhenProcessEnds() throws Exception {
StorageLayer.close();
- String[] args = { "../" };
+ String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args, false);
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{EE_FEATURES.MULTI_TENANCY});
@@ -315,7 +314,7 @@ public void confirmHikariLoggerClosedOnlyWhenProcessEnds() throws Exception {
@Test
public void testDBPasswordMaskingOnDBConnectionFailUsingConnectionUri() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
String dbUser = "db_user";
String dbPassword = "db_password";
@@ -347,7 +346,7 @@ public void testDBPasswordMaskingOnDBConnectionFailUsingConnectionUri() throws E
@Test
public void testDBPasswordMaskingOnDBConnectionFailUsingCredentials() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
String dbUser = "db_user";
String dbPassword = "db_password";
@@ -381,7 +380,7 @@ public void testDBPasswordMaskingOnDBConnectionFailUsingCredentials() throws Exc
@Test
public void testDBPasswordMasking() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
ByteArrayOutputStream stdOutput = new ByteArrayOutputStream();
ByteArrayOutputStream errorOutput = new ByteArrayOutputStream();
@@ -416,7 +415,7 @@ public void testDBPasswordMasking() throws Exception {
@Test
public void testDBPasswordIsNotLoggedWhenProcessStartsEnds() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
Utils.setValueInConfig("error_log_path", "null");
Utils.setValueInConfig("info_log_path", "null");
@@ -479,7 +478,7 @@ public void testDBPasswordIsNotLoggedWhenProcessStartsEnds() throws Exception {
@Test
public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception {
- String[] args = { "../" };
+ String[] args = {"../"};
Utils.setValueInConfig("error_log_path", "null");
Utils.setValueInConfig("info_log_path", "null");
@@ -505,8 +504,8 @@ public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception {
Main main = process.getProcess();
FeatureFlagTestContent.getInstance(main)
- .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[] {
- EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY });
+ .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
+ EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
JsonObject config = new JsonObject();
TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null);
@@ -548,8 +547,8 @@ public void testDBPasswordIsNotLoggedWhenTenantIsCreated() throws Exception {
Main main = process.getProcess();
FeatureFlagTestContent.getInstance(main)
- .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[] {
- EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY });
+ .setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
+ EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null);
JsonObject config = new JsonObject();
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java
index 37346a11..ea29a8f9 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/OneMillionUsersTest.java
@@ -27,8 +27,11 @@
import io.supertokens.emailpassword.EmailPassword;
import io.supertokens.emailpassword.ParsedFirebaseSCryptResponse;
import io.supertokens.featureflag.EE_FEATURES;
+import io.supertokens.featureflag.FeatureFlag;
import io.supertokens.featureflag.FeatureFlagTestContent;
import io.supertokens.passwordless.Passwordless;
+import io.supertokens.pluginInterface.RECIPE_ID;
+import io.supertokens.pluginInterface.Storage;
import io.supertokens.pluginInterface.authRecipe.AuthRecipeUserInfo;
import io.supertokens.pluginInterface.authRecipe.LoginMethod;
import io.supertokens.pluginInterface.authRecipe.sqlStorage.AuthRecipeSQLStorage;
@@ -39,6 +42,7 @@
import io.supertokens.pluginInterface.thirdparty.sqlStorage.ThirdPartySQLStorage;
import io.supertokens.session.Session;
import io.supertokens.session.info.SessionInformationHolder;
+import io.supertokens.storage.postgresql.Start;
import io.supertokens.storage.postgresql.test.httpRequest.HttpRequestForTesting;
import io.supertokens.storageLayer.StorageLayer;
import io.supertokens.thirdparty.ThirdParty;
@@ -107,7 +111,8 @@ private void createEmailPasswordUsers(Main main) throws Exception {
String userId = io.supertokens.utils.Utils.getUUID();
long timeJoined = System.currentTimeMillis();
- storage.signUp(TenantIdentifier.BASE_TENANT, userId, "eptest" + finalI + "@example.com", combinedPasswordHash,
+ storage.signUp(TenantIdentifier.BASE_TENANT, userId, "eptest" + finalI + "@example.com",
+ combinedPasswordHash,
timeJoined);
synchronized (lock) {
allUserIds.add(userId);
@@ -116,7 +121,7 @@ private void createEmailPasswordUsers(Main main) throws Exception {
throw new RuntimeException(e);
}
if (finalI % 10000 == 9999) {
- System.out.println("Created " + ((finalI +1)) + " users");
+ System.out.println("Created " + ((finalI + 1)) + " users");
}
});
}
@@ -137,7 +142,8 @@ private void createPasswordlessUsersWithEmail(Main main) throws Exception {
String userId = io.supertokens.utils.Utils.getUUID();
long timeJoined = System.currentTimeMillis();
try {
- storage.createUser(TenantIdentifier.BASE_TENANT, userId, "pltest" + finalI + "@example.com", null, timeJoined);
+ storage.createUser(TenantIdentifier.BASE_TENANT, userId, "pltest" + finalI + "@example.com", null,
+ timeJoined);
synchronized (lock) {
allUserIds.add(userId);
}
@@ -146,7 +152,7 @@ private void createPasswordlessUsersWithEmail(Main main) throws Exception {
}
if (finalI % 10000 == 9999) {
- System.out.println("Created " + ((finalI +1)) + " users");
+ System.out.println("Created " + ((finalI + 1)) + " users");
}
});
}
@@ -176,7 +182,7 @@ private void createPasswordlessUsersWithPhone(Main main) throws Exception {
}
if (finalI % 10000 == 9999) {
- System.out.println("Created " + ((finalI +1)) + " users");
+ System.out.println("Created " + ((finalI + 1)) + " users");
}
});
}
@@ -198,7 +204,8 @@ private void createThirdpartyUsers(Main main) throws Exception {
long timeJoined = System.currentTimeMillis();
try {
- storage.signUp(TenantIdentifier.BASE_TENANT, userId, "tptest" + finalI + "@example.com", new LoginMethod.ThirdParty("google", "googleid" + finalI), timeJoined );
+ storage.signUp(TenantIdentifier.BASE_TENANT, userId, "tptest" + finalI + "@example.com",
+ new LoginMethod.ThirdParty("google", "googleid" + finalI), timeJoined);
synchronized (lock) {
allUserIds.add(userId);
}
@@ -207,7 +214,7 @@ private void createThirdpartyUsers(Main main) throws Exception {
}
if (finalI % 10000 == 9999) {
- System.out.println("Created " + (finalI +1) + " users");
+ System.out.println("Created " + (finalI + 1) + " users");
}
});
}
@@ -383,6 +390,30 @@ private void createSessions(Main main) throws Exception {
es.awaitTermination(10, TimeUnit.MINUTES);
}
+ private void createActiveUserEntries(Main main) throws Exception {
+ System.out.println("Creating active user entries...");
+
+ ExecutorService es = Executors.newFixedThreadPool(NUM_THREADS);
+
+ for (String userId : allPrimaryUserIds) {
+ String finalUserId = userId;
+ es.execute(() -> {
+ try {
+ Storage storage = StorageLayer.getBaseStorage(main);
+ Start start = (Start) storage;
+
+ start.updateLastActive(new AppIdentifier(null, null), finalUserId, System.currentTimeMillis() - new Random().nextInt(1000 * 3600 * 24 * 60));
+
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ es.shutdown();
+ es.awaitTermination(10, TimeUnit.MINUTES);
+ }
+
@Test
public void testCreatingOneMillionUsers() throws Exception {
if (System.getenv("ONE_MILLION_USERS_TEST") == null) {
@@ -397,7 +428,7 @@ public void testCreatingOneMillionUsers() throws Exception {
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
- EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
+ EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY, EE_FEATURES.MFA, EE_FEATURES.DASHBOARD_LOGIN});
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
@@ -442,6 +473,13 @@ public void testCreatingOneMillionUsers() throws Exception {
System.out.println("Time taken to create sessions: " + ((en - st) / 1000) + " sec");
}
+ {
+ long st = System.currentTimeMillis();
+ createActiveUserEntries(process.getProcess());
+ long en = System.currentTimeMillis();
+ System.out.println("Time taken to create active user entries: " + ((en - st) / 1000) + " sec");
+ }
+
sanityCheckAPIs(process.getProcess());
allUserIds.clear();
allPrimaryUserIds.clear();
@@ -463,7 +501,7 @@ public void testCreatingOneMillionUsers() throws Exception {
FeatureFlagTestContent.getInstance(process.getProcess())
.setKeyValue(FeatureFlagTestContent.ENABLED_FEATURES, new EE_FEATURES[]{
- EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY});
+ EE_FEATURES.ACCOUNT_LINKING, EE_FEATURES.MULTI_TENANCY, EE_FEATURES.MFA, EE_FEATURES.DASHBOARD_LOGIN});
process.startProcess();
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
@@ -646,7 +684,8 @@ private void sanityCheckAPIs(Main main) throws Exception {
JsonArray userRolesArr = response.getAsJsonArray("roles");
assertEquals(1, userRolesArr.size());
assertTrue(
- userRolesArr.get(0).getAsString().equals("admin") || userRolesArr.get(0).getAsString().equals("user")
+ userRolesArr.get(0).getAsString().equals("admin") ||
+ userRolesArr.get(0).getAsString().equals("user")
);
}
@@ -791,7 +830,8 @@ private void measureOperations(Main main) throws Exception {
int finalI = i;
es.execute(() -> {
try {
- ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, "twitter" + finalI + "@example.com");
+ ThirdParty.signInUp(main, "twitter", "twitterid" + finalI,
+ "twitter" + finalI + "@example.com");
} catch (Exception e) {
errorCount.incrementAndGet();
throw new RuntimeException(e);
@@ -817,7 +857,8 @@ private void measureOperations(Main main) throws Exception {
int finalI = i;
es.execute(() -> {
try {
- ThirdParty.signInUp(main, "twitter", "twitterid" + finalI, "twitter" + finalI + "@example.com");
+ ThirdParty.signInUp(main, "twitter", "twitterid" + finalI,
+ "twitter" + finalI + "@example.com");
} catch (Exception e) {
errorCount.incrementAndGet();
throw new RuntimeException(e);
@@ -882,6 +923,36 @@ private void measureOperations(Main main) throws Exception {
return null;
});
System.out.println("Update user metadata " + time);
+ assert time < 3000;
+ }
+
+ { // measure user counting
+ long time = measureTime(() -> {
+ try {
+ AuthRecipe.getUsersCount(main, null);
+ AuthRecipe.getUsersCount(main, new RECIPE_ID[]{RECIPE_ID.EMAIL_PASSWORD});
+ AuthRecipe.getUsersCount(main, new RECIPE_ID[]{RECIPE_ID.EMAIL_PASSWORD, RECIPE_ID.THIRD_PARTY});
+ } catch (Exception e) {
+ errorCount.incrementAndGet();
+ throw new RuntimeException(e);
+ }
+ return null;
+ });
+ System.out.println("User counting: " + time);
+ assert time < 10000;
+ }
+ { // measure telemetry
+ long time = measureTime(() -> {
+ try {
+ FeatureFlag.getInstance(main).getPaidFeatureStats();
+ } catch (Exception e) {
+ errorCount.incrementAndGet();
+ throw new RuntimeException(e);
+ }
+ return null;
+ });
+ System.out.println("Telemetry: " + time);
+ assert time < 6000;
}
assertEquals(0, errorCount.get());
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java b/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java
new file mode 100644
index 00000000..21a86f60
--- /dev/null
+++ b/src/test/java/io/supertokens/storage/postgresql/test/PostgresSQLConfigTest.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2024, VRAI Labs and/or its affiliates. All rights reserved.
+ *
+ * This software is licensed under the Apache License, Version 2.0 (the
+ * "License") as published by the Apache Software Foundation.
+ *
+ * You may not use this file except in compliance with the License. You may
+ * obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package io.supertokens.storage.postgresql.test;
+
+import io.supertokens.ProcessState;
+import io.supertokens.storage.postgresql.annotations.DashboardInfo;
+import io.supertokens.storage.postgresql.config.PostgreSQLConfig;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TestRule;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+
+import static org.junit.Assert.*;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.lang.reflect.Field;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+public class PostgresSQLConfigTest {
+
+ @Rule
+ public TestRule watchman = Utils.getOnFailure();
+
+ @AfterClass
+ public static void afterTesting() {
+ Utils.afterTesting();
+ }
+
+ @Before
+ public void beforeEach() {
+ Utils.reset();
+ }
+
+ @Test
+ public void testMatchConfigPropertiesDescription() throws Exception {
+ String[] args = {"../"};
+
+ TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
+ assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
+
+ // Skipping postgresql_config_version because it doesn't
+ // have a description in the config.yaml file
+ String[] ignoredProperties = {"postgresql_config_version"};
+
+ // Match the descriptions in the config.yaml file with the descriptions in the
+ // CoreConfig class
+ matchYamlAndConfigDescriptions("./config.yaml", ignoredProperties);
+
+ // Match the descriptions in the devConfig.yaml file with the descriptions in
+ // the CoreConfig class
+ String[] devConfigIgnoredProperties = Arrays.copyOf(ignoredProperties, ignoredProperties.length + 2);
+ // We ignore these properties in devConfig.yaml because it has a different
+ // description
+ // in devConfig.yaml and has a default value
+ devConfigIgnoredProperties[ignoredProperties.length] = "postgresql_user";
+ devConfigIgnoredProperties[ignoredProperties.length + 1] = "postgresql_password";
+ matchYamlAndConfigDescriptions("./devConfig.yaml", devConfigIgnoredProperties);
+
+ process.kill();
+ assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
+ }
+
+ private void matchYamlAndConfigDescriptions(String path, String[] ignoreProperties) throws Exception {
+ try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
+ // Get the content of the file as string
+ String content = reader.lines().collect(Collectors.joining(System.lineSeparator()));
+ // Find the line that contains 'postgresql_config_version', and then split
+ // the file after that line
+ String allProperties = content.split("postgresql_config_version:\\s*\\d+\n")[1];
+
+ // Split by all the other allProperties string by new line
+ String[] properties = allProperties.split("\n\n");
+ // This will contain the description of each property from the yaml file
+ Map propertyDescriptions = new HashMap();
+
+ System.out.println("Last property: " + properties[properties.length - 1] + "\n\n");
+
+ for (int i = 0; i < properties.length; i++) {
+ String possibleProperty = properties[i].trim();
+ String[] lines = possibleProperty.split("\n");
+ // This ensures that it is a property with a description as a comment
+ // at the top
+ if (lines[lines.length - 1].endsWith(":")) {
+ String propertyKeyString = lines[lines.length - 1];
+ // Remove the comment "# " from the start
+ String propertyKey = propertyKeyString.substring(2, propertyKeyString.length() - 1);
+ String propertyDescription = "";
+ // Remove the comment "# " from the start and merge all the lines to form the
+ // description
+ for (int j = 0; j < lines.length - 1; j++) {
+ propertyDescription = propertyDescription + " " + lines[j].substring(2);
+ }
+ propertyDescription = propertyDescription.trim();
+
+ propertyDescriptions.put(propertyKey, propertyDescription);
+ }
+ }
+
+ for (String fieldId : PostgreSQLConfig.getValidFields()) {
+ if (Arrays.asList(ignoreProperties).contains(fieldId)) {
+ continue;
+ }
+
+ Field field = PostgreSQLConfig.class.getDeclaredField(fieldId);
+
+ // Skip fields that are not annotated with JsonProperty
+ if (!field.isAnnotationPresent(JsonProperty.class)) {
+ continue;
+ }
+
+
+ String valueInfo = "";
+ if (field.getType() == String.class) {
+ valueInfo = "string value.";
+ } else if (field.getType() == int.class || field.getType() == Integer.class) {
+ valueInfo = "integer value.";
+ } else if (field.getType() == long.class) {
+ valueInfo = "long value.";
+ } else if (field.getType() == boolean.class || field.getType() == Boolean.class) {
+ valueInfo = "boolean value.";
+ }
+
+ String descriptionInConfig = field.getAnnotation(DashboardInfo.class).description();
+ descriptionInConfig = "(DIFFERENT_ACROSS_TENANTS" +
+ (field.getAnnotation(DashboardInfo.class).isOptional() ? " | OPTIONAL" : " | COMPULSORY")
+ + (field.getAnnotation(DashboardInfo.class).isOptional() ?
+ " | Default: " + field.getAnnotation(DashboardInfo.class).defaultValue() : "")
+ + ") "
+ + valueInfo + " "
+ + descriptionInConfig;
+ String descriptionInYaml = propertyDescriptions.get(fieldId);
+
+ if (descriptionInYaml == null) {
+ fail("Unable to find description or property for " + fieldId + " in " + path + " file");
+ }
+
+ // Assert that description in yaml contains the description in config
+ assertEquals("For " + fieldId, descriptionInYaml.trim(), descriptionInConfig.trim());
+ }
+ }
+ }
+
+}
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java
index 8f6d1699..e2ee74f2 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/StorageLayerTest.java
@@ -115,13 +115,18 @@ public void testLinkedAccountUser() throws Exception {
AuthRecipeUserInfo user1 = EmailPassword.signUp(process.getProcess(), "test1@example.com", "password");
Thread.sleep(50);
- AuthRecipeUserInfo user2 = ThirdParty.signInUp(process.getProcess(), "google", "googleid", "test2@example.com").user;
+ AuthRecipeUserInfo user2 = ThirdParty.signInUp(process.getProcess(), "google", "googleid",
+ "test2@example.com").user;
Thread.sleep(50);
- Passwordless.CreateCodeResponse code1 = Passwordless.createCode(process.getProcess(), "test3@example.com", null, null, null);
- AuthRecipeUserInfo user3 = Passwordless.consumeCode(process.getProcess(), code1.deviceId, code1.deviceIdHash, code1.userInputCode, null).user;
+ Passwordless.CreateCodeResponse code1 = Passwordless.createCode(process.getProcess(), "test3@example.com", null,
+ null, null);
+ AuthRecipeUserInfo user3 = Passwordless.consumeCode(process.getProcess(), code1.deviceId, code1.deviceIdHash,
+ code1.userInputCode, null).user;
Thread.sleep(50);
- Passwordless.CreateCodeResponse code2 = Passwordless.createCode(process.getProcess(), null, "+919876543210", null, null);
- AuthRecipeUserInfo user4 = Passwordless.consumeCode(process.getProcess(), code2.deviceId, code2.deviceIdHash, code2.userInputCode, null).user;
+ Passwordless.CreateCodeResponse code2 = Passwordless.createCode(process.getProcess(), null, "+919876543210",
+ null, null);
+ AuthRecipeUserInfo user4 = Passwordless.consumeCode(process.getProcess(), code2.deviceId, code2.deviceIdHash,
+ code2.userInputCode, null).user;
AuthRecipe.createPrimaryUser(process.getProcess(), user3.getSupertokensUserId());
AuthRecipe.linkAccounts(process.getProcess(), user1.getSupertokensUserId(), user3.getSupertokensUserId());
@@ -135,8 +140,9 @@ public void testLinkedAccountUser() throws Exception {
user4.getSupertokensUserId()
};
- for (String userId : userIds){
- AuthRecipeUserInfo primaryUser = ((AuthRecipeStorage) StorageLayer.getStorage(process.getProcess())).getPrimaryUserById(
+ for (String userId : userIds) {
+ AuthRecipeUserInfo primaryUser = ((AuthRecipeStorage) StorageLayer.getStorage(
+ process.getProcess())).getPrimaryUserById(
new AppIdentifier(null, null), userId);
assertEquals(user3.getSupertokensUserId(), primaryUser.getSupertokensUserId());
assertEquals(4, primaryUser.loginMethods.length);
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java b/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java
index 51673061..eb135240 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/SuperTokensSaaSSecretTest.java
@@ -86,11 +86,12 @@ public void testThatTenantCannotSetDatabaseRelatedConfigIfSuperTokensSaaSSecretI
try {
JsonObject j = new JsonObject();
j.addProperty(PROTECTED_DB_CONFIG[i], "");
- Multitenancy.addNewOrUpdateAppOrTenant(process.main, new TenantConfig(new TenantIdentifier(null, null, "t1"), new EmailPasswordConfig(false),
- new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
- new PasswordlessConfig(false),
- null, null,
- j), true);
+ Multitenancy.addNewOrUpdateAppOrTenant(process.main,
+ new TenantConfig(new TenantIdentifier(null, null, "t1"), new EmailPasswordConfig(false),
+ new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
+ new PasswordlessConfig(false),
+ null, null,
+ j), true);
fail();
} catch (BadPermissionException e) {
assertEquals(e.getMessage(), "Not allowed to modify DB related configs.");
@@ -162,12 +163,13 @@ public void testThatTenantCanSetDatabaseRelatedConfigIfSuperTokensSaaSSecretIsNo
} else if (PROTECTED_DB_CONFIG_VALUES[i] instanceof Integer) {
j.addProperty(PROTECTED_DB_CONFIG[i], (Integer) PROTECTED_DB_CONFIG_VALUES[i]);
}
- Multitenancy.addNewOrUpdateAppOrTenant(process.main, new TenantConfig(new TenantIdentifier(null, null, "t1"),
- new EmailPasswordConfig(false),
- new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
- new PasswordlessConfig(false),
- null, null,
- j), false);
+ Multitenancy.addNewOrUpdateAppOrTenant(process.main,
+ new TenantConfig(new TenantIdentifier(null, null, "t1"),
+ new EmailPasswordConfig(false),
+ new ThirdPartyConfig(false, new ThirdPartyConfig.Provider[0]),
+ new PasswordlessConfig(false),
+ null, null,
+ j), false);
}
JsonObject coreConfig = new JsonObject();
@@ -224,7 +226,8 @@ public void testThatTenantCannotGetDatabaseRelatedConfigIfSuperTokensSaaSSecretI
{
JsonObject response = HttpRequestForTesting.sendJsonRequest(process.getProcess(), "",
- HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, "/recipe/multitenancy/tenant/list"),
+ HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT,
+ "/recipe/multitenancy/tenant/list"),
null, 1000, 1000, null,
SemVer.v3_0.get(), "GET", apiKey, "multitenancy");
@@ -245,7 +248,8 @@ public void testThatTenantCannotGetDatabaseRelatedConfigIfSuperTokensSaaSSecretI
{
JsonObject response = HttpRequestForTesting.sendJsonRequest(process.getProcess(), "",
- HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT, "/recipe/multitenancy/tenant/list"),
+ HttpRequestForTesting.getMultitenantUrl(TenantIdentifier.BASE_TENANT,
+ "/recipe/multitenancy/tenant/list"),
null, 1000, 1000, null,
SemVer.v3_0.get(), "GET", saasSecret, "multitenancy");
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java b/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java
index f215a4c0..81f989c8 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/TableCreationTest.java
@@ -45,7 +45,7 @@ public void beforeEach() {
@Test
public void checkingCreationOfNewTable() throws InterruptedException {
- String[] args = { "../" };
+ String[] args = {"../"};
TestingProcessManager.TestingProcess process = TestingProcessManager.start(args);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java b/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java
index 5dec4454..4c3f6e25 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/TestMainThread.java
@@ -75,8 +75,9 @@ public void testThatMainThreadIsSameThroughout() throws Exception {
JsonObject requestBody = new JsonObject();
requestBody.addProperty("appId", "a1");
requestBody.add("coreConfig", config);
- HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "", "http://localhost:3567/recipe/multitenancy/app",
- requestBody, 1000, 2500, null, "3.0", "multitenancy");
+ HttpRequestForTesting.sendJsonPUTRequest(process.getProcess(), "",
+ "http://localhost:3567/recipe/multitenancy/app",
+ requestBody, 1000, 2500, null, "3.0", "multitenancy");
Storage storage2 = StorageLayer.getStorage(new TenantIdentifier(null, "a1", null), process.getProcess());
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java b/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java
index 5ca0aa3e..52e1f7cf 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/httpRequest/HttpRequestForTesting.java
@@ -62,7 +62,8 @@ private static boolean isJsonValid(String jsonInString) {
@SuppressWarnings("unchecked")
public static T sendGETRequest(Main main, String requestID, String url, Map params,
- int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid)
+ int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion,
+ String rid)
throws IOException, HttpResponseException {
StringBuilder paramBuilder = new StringBuilder();
@@ -198,101 +199,107 @@ public static T sendJsonRequest(Main main, String requestID, String url, Jso
}
public static T sendJsonPOSTRequest(Main main, String requestID, String url, JsonElement requestBody,
- int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid)
+ int connectionTimeoutMS, int readTimeoutMS, Integer version,
+ String cdiVersion, String rid)
throws IOException, HttpResponseException {
return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version,
cdiVersion, "POST", null, rid);
}
public static T sendJsonPOSTRequest(Main main, String requestID, String url, JsonElement requestBody,
- int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String apiKey, String rid)
+ int connectionTimeoutMS, int readTimeoutMS, Integer version,
+ String cdiVersion, String apiKey, String rid)
throws IOException, HttpResponseException {
return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version,
cdiVersion, "POST", apiKey, rid);
}
public static T sendJsonPUTRequest(Main main, String requestID, String url, JsonElement requestBody,
- int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid)
+ int connectionTimeoutMS, int readTimeoutMS, Integer version,
+ String cdiVersion, String rid)
throws IOException, HttpResponseException {
return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version,
cdiVersion, "PUT", null, rid);
}
public static T sendJsonDELETERequest(Main main, String requestID, String url, JsonElement requestBody,
- int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid)
+ int connectionTimeoutMS, int readTimeoutMS, Integer version,
+ String cdiVersion, String rid)
throws IOException, HttpResponseException {
return sendJsonRequest(main, requestID, url, requestBody, connectionTimeoutMS, readTimeoutMS, version,
cdiVersion, "DELETE", null, rid);
}
@SuppressWarnings("unchecked")
- public static T sendJsonDELETERequestWithQueryParams(Main main, String requestID, String url, Map params,
- int connectionTimeoutMS, int readTimeoutMS, Integer version, String cdiVersion, String rid)
+ public static T sendJsonDELETERequestWithQueryParams(Main main, String requestID, String url,
+ Map params,
+ int connectionTimeoutMS, int readTimeoutMS,
+ Integer version, String cdiVersion, String rid)
throws IOException, HttpResponseException {
- StringBuilder paramBuilder = new StringBuilder();
+ StringBuilder paramBuilder = new StringBuilder();
- if (params != null) {
- for (Map.Entry entry : params.entrySet()) {
- paramBuilder.append(entry.getKey()).append("=")
- .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("&");
- }
- }
- String paramsStr = paramBuilder.toString();
- if (!paramsStr.equals("")) {
- paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
- url = url + "?" + paramsStr;
+ if (params != null) {
+ for (Map.Entry entry : params.entrySet()) {
+ paramBuilder.append(entry.getKey()).append("=")
+ .append(URLEncoder.encode(entry.getValue(), StandardCharsets.UTF_8)).append("&");
+ }
+ }
+ String paramsStr = paramBuilder.toString();
+ if (!paramsStr.equals("")) {
+ paramsStr = paramsStr.substring(0, paramsStr.length() - 1);
+ url = url + "?" + paramsStr;
+ }
+ URL obj = getURL(main, requestID, url);
+ InputStream inputStream = null;
+ HttpURLConnection con = null;
+
+ try {
+ con = (HttpURLConnection) obj.openConnection();
+ con.setRequestMethod("DELETE");
+ con.setConnectTimeout(connectionTimeoutMS);
+ con.setReadTimeout(readTimeoutMS + 1000);
+ if (version != null) {
+ con.setRequestProperty("api-version", version + "");
+ }
+ if (cdiVersion != null) {
+ con.setRequestProperty("cdi-version", cdiVersion);
+ }
+ if (rid != null) {
+ con.setRequestProperty("rId", rid);
+ }
+
+ int responseCode = con.getResponseCode();
+
+ if (responseCode < STATUS_CODE_ERROR_THRESHOLD) {
+ inputStream = con.getInputStream();
+ } else {
+ inputStream = con.getErrorStream();
+ }
+
+ StringBuilder response = new StringBuilder();
+ try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
+ String inputLine;
+ while ((inputLine = in.readLine()) != null) {
+ response.append(inputLine);
}
- URL obj = getURL(main, requestID, url);
- InputStream inputStream = null;
- HttpURLConnection con = null;
-
- try {
- con = (HttpURLConnection) obj.openConnection();
- con.setRequestMethod("DELETE");
- con.setConnectTimeout(connectionTimeoutMS);
- con.setReadTimeout(readTimeoutMS + 1000);
- if (version != null) {
- con.setRequestProperty("api-version", version + "");
- }
- if (cdiVersion != null) {
- con.setRequestProperty("cdi-version", cdiVersion);
- }
- if (rid != null) {
- con.setRequestProperty("rId", rid);
- }
-
- int responseCode = con.getResponseCode();
-
- if (responseCode < STATUS_CODE_ERROR_THRESHOLD) {
- inputStream = con.getInputStream();
- } else {
- inputStream = con.getErrorStream();
- }
-
- StringBuilder response = new StringBuilder();
- try (BufferedReader in = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
- String inputLine;
- while ((inputLine = in.readLine()) != null) {
- response.append(inputLine);
- }
- }
- if (responseCode < STATUS_CODE_ERROR_THRESHOLD) {
- if (!isJsonValid(response.toString())) {
- return (T) response.toString();
- }
- return (T) (new JsonParser().parse(response.toString()));
- }
- throw new HttpResponseException(responseCode, response.toString());
- } finally {
- if (inputStream != null) {
- inputStream.close();
- }
-
- if (con != null) {
- con.disconnect();
- }
+ }
+ if (responseCode < STATUS_CODE_ERROR_THRESHOLD) {
+ if (!isJsonValid(response.toString())) {
+ return (T) response.toString();
}
+ return (T) (new JsonParser().parse(response.toString()));
+ }
+ throw new HttpResponseException(responseCode, response.toString());
+ } finally {
+ if (inputStream != null) {
+ inputStream.close();
+ }
+
+ if (con != null) {
+ con.disconnect();
+ }
}
+ }
public static String getMultitenantUrl(TenantIdentifier tenantIdentifier, String path) {
StringBuilder sb = new StringBuilder();
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java
index 97a3fb6b..dc592a21 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/StorageLayerTest.java
@@ -49,7 +49,6 @@
import java.io.IOException;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
@@ -175,11 +174,12 @@ public void storageInstanceIsReusedAcrossTenants()
StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess()));
Assert.assertEquals(
- Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()).getAccessTokenValidity(),
+ Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess())
+ .getAccessTokenValidityInMillis(),
(long) 3600 * 1000);
Assert.assertEquals(Config.getConfig(new TenantIdentifier(null, "abc", null), process.getProcess())
- .getAccessTokenValidity(),
+ .getAccessTokenValidityInMillis(),
(long) 3601 * 1000);
Assert.assertEquals(
@@ -239,11 +239,12 @@ public void storageInstanceIsReusedAcrossTenantsComplex()
StorageLayer.getStorage(new TenantIdentifier(null, null, null), process.getProcess()));
Assert.assertEquals(
- Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()).getAccessTokenValidity(),
+ Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess())
+ .getAccessTokenValidityInMillis(),
(long) 3600 * 1000);
Assert.assertEquals(Config.getConfig(new TenantIdentifier(null, "abc", null), process.getProcess())
- .getAccessTokenValidity(),
+ .getAccessTokenValidityInMillis(),
(long) 3601 * 1000);
Assert.assertEquals(
@@ -329,7 +330,8 @@ public void mergingTenantWithBaseConfigWithConflictingConfigsThrowsError()
fail();
} catch (InvalidConfigException e) {
assertEquals(e.getMessage(),
- "You cannot set different values for postgresql_thirdparty_users_table_name for the same user pool");
+ "You cannot set different values for postgresql_thirdparty_users_table_name for the same user " +
+ "pool");
}
process.kill();
@@ -454,11 +456,12 @@ public void newStorageIsNotCreatedWhenSameTenantIsAdded()
existingStorage);
Assert.assertEquals(
- Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess()).getAccessTokenValidity(),
+ Config.getConfig(new TenantIdentifier(null, null, null), process.getProcess())
+ .getAccessTokenValidityInMillis(),
(long) 3600 * 1000);
Assert.assertEquals(Config.getConfig(new TenantIdentifier(null, "abc", null), process.getProcess())
- .getAccessTokenValidity(),
+ .getAccessTokenValidityInMillis(),
(long) 3601 * 1000);
Assert.assertEquals(
@@ -806,7 +809,8 @@ public void testTenantCreationAndThenDbDownDbThrowsErrorInRecipesAndDoesntAffect
tenantConfigJson);
StorageLayer.getMultitenancyStorage(process.getProcess()).createTenant(tenantConfig);
- MultitenancyHelper.getInstance(process.getProcess()).refreshTenantsInCoreBasedOnChangesInCoreConfigOrIfTenantListChanged(true);
+ MultitenancyHelper.getInstance(process.getProcess())
+ .refreshTenantsInCoreBasedOnChangesInCoreConfigOrIfTenantListChanged(true);
try {
EmailPassword.signIn(tid, (StorageLayer.getStorage(tid, process.getProcess())),
diff --git a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java
index b8635224..5081c414 100644
--- a/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java
+++ b/src/test/java/io/supertokens/storage/postgresql/test/multitenancy/TestForNoCrashDuringStartup.java
@@ -107,24 +107,27 @@ public void testThatCUDRecoversWhenItFailsToAddEntryDuringCreation() throws Exce
assertEquals(2, allTenants.length); // should have the new CUD
try {
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
fail();
} catch (HttpResponseException e) {
// ignore
- assertTrue(e.getMessage().contains("Internal Error")); // retried creating tenant entry
+ assertEquals("Http error. Status Code: 500. Message: java.sql.SQLException: Simulated error in addTenantIdInTargetStorage", e.getMessage());
}
MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false;
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
}
@Test
public void testThatCUDRecoversWhenTheDbIsDownDuringCreationButDbComesUpLater() throws Exception {
Start start = ((Start) StorageLayer.getBaseStorage(process.getProcess()));
try {
- update(start, "DROP DATABASE st5000;", pst -> {});
+ update(start, "DROP DATABASE st5000;", pst -> {
+ });
} catch (Exception e) {
// ignore
}
@@ -147,24 +150,30 @@ public void testThatCUDRecoversWhenTheDbIsDownDuringCreationButDbComesUpLater()
fail();
} catch (StorageQueryException e) {
// ignore
- assertEquals("java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage());
+ assertEquals(
+ "java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to " +
+ "initialize pool: FATAL: database \"st5000\" does not exist",
+ e.getMessage());
}
TenantConfig[] allTenants = MultitenancyHelper.getInstance(process.getProcess()).getAllTenants();
assertEquals(2, allTenants.length); // should have the new CUD
try {
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
fail();
} catch (HttpResponseException e) {
// ignore
- assertTrue(e.getMessage().contains("Internal Error")); // db is still down
+ assertEquals("Http error. Status Code: 500. Message: java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage());
}
- update(start, "CREATE DATABASE st5000;", pst -> {});
+ update(start, "CREATE DATABASE st5000;", pst -> {
+ });
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
}
@Test
@@ -260,7 +269,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddEntry
MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false;
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
}
@Test
@@ -291,7 +301,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenTenantEntryIsInconsistentIn
assertEquals(2, allTenants.length); // should have the new CUD
Start start = (Start) StorageLayer.getBaseStorage(process.getProcess());
- update(start, "DELETE FROM apps;", pst -> {});
+ update(start, "DELETE FROM apps;", pst -> {
+ });
process.kill(false);
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STOPPED));
@@ -306,13 +317,15 @@ public void testThatCoreDoesNotCrashDuringStartupWhenTenantEntryIsInconsistentIn
MultitenancyQueries.simulateErrorInAddingTenantIdInTargetStorage_forTesting = false;
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
Session.createNewSession(process.getProcess(), "userid", new JsonObject(), new JsonObject());
}
@Test
- public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntryInTheBaseTenantStorage() throws Exception {
+ public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntryInTheBaseTenantStorage()
+ throws Exception {
JsonObject coreConfig = new JsonObject();
TenantIdentifier tenantIdentifier = new TenantIdentifier(null, "a1", null);
@@ -354,7 +367,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntry
assertNotNull(process.checkOrWaitForEvent(ProcessState.PROCESS_STATE.STARTED));
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
Session.createNewSession(
new TenantIdentifier(null, "a1", null),
@@ -364,7 +378,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenAppCreationFailedToAddEntry
}
@Test
- public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenantEntryInTargetStorageWithLoadOnlyCUDConfig() throws Exception {
+ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenantEntryInTargetStorageWithLoadOnlyCUDConfig()
+ throws Exception {
JsonObject coreConfig = new JsonObject();
TenantIdentifier tenantIdentifier = new TenantIdentifier("127.0.0.1", null, null);
@@ -442,10 +457,12 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan
assertEquals(2, allTenants.length); // should have the new CUD
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
try {
- tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
fail();
} catch (HttpResponseException e) {
// ignore
@@ -465,9 +482,11 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan
assertEquals(2, allTenants.length); // should have the new CUD
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("localhost", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
try {
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
fail();
} catch (HttpResponseException e) {
// ignore
@@ -488,7 +507,8 @@ public void testThatCoreDoesNotCrashDuringStartupWhenCUDCreationFailedToAddTenan
public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throws Exception {
Start start = ((Start) StorageLayer.getBaseStorage(process.getProcess()));
try {
- update(start, "DROP DATABASE st5000;", pst -> {});
+ update(start, "DROP DATABASE st5000;", pst -> {
+ });
} catch (Exception e) {
// ignore
}
@@ -511,18 +531,22 @@ public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throw
fail();
} catch (StorageQueryException e) {
// ignore
- assertEquals("java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage());
+ assertEquals(
+ "java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to " +
+ "initialize pool: FATAL: database \"st5000\" does not exist",
+ e.getMessage());
}
TenantConfig[] allTenants = MultitenancyHelper.getInstance(process.getProcess()).getAllTenants();
assertEquals(2, allTenants.length); // should have the new CUD
try {
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
fail();
} catch (HttpResponseException e) {
// ignore
- assertTrue(e.getMessage().contains("Internal Error")); // db is still down
+ assertEquals("Http error. Status Code: 500. Message: java.sql.SQLException: com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Failed to initialize pool: FATAL: database \"st5000\" does not exist", e.getMessage());
}
process.kill(false);
@@ -537,13 +561,16 @@ public void testThatTenantComesToLifeOnceTheTargetDbIsUpAfterCoreRestart() throw
// the process should start successfully even though the db is down
start = ((Start) StorageLayer.getBaseStorage(process.getProcess()));
- update(start, "CREATE DATABASE st5000;", pst -> {});
+ update(start, "CREATE DATABASE st5000;", pst -> {
+ });
// this should succeed now
- tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1", "test@example.com", process.getProcess(), SemVer.v5_0);
+ tpSignInUpAndGetResponse(new TenantIdentifier("127.0.0.1", null, null), "google", "googleid1",
+ "test@example.com", process.getProcess(), SemVer.v5_0);
}
- public static JsonObject tpSignInUpAndGetResponse(TenantIdentifier tenantIdentifier, String thirdPartyId, String thirdPartyUserId, String email, Main main, SemVer version)
+ public static JsonObject tpSignInUpAndGetResponse(TenantIdentifier tenantIdentifier, String thirdPartyId,
+ String thirdPartyUserId, String email, Main main, SemVer version)
throws HttpResponseException, IOException {
JsonObject emailObject = new JsonObject();
emailObject.addProperty("id", email);