From 044969b6e01a54b10893bc2632ec1e095008e47f Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 09:50:22 -0500 Subject: [PATCH 01/13] Update CreateClientAccessToken to check IsApproved status --- .../0176-Update-CreateClientAccessToken.sql | 48 +++++++++++++++++++ .../0176-Update-CreateClientAccessToken.sql | 48 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql create mode 100644 Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql diff --git a/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql b/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql new file mode 100644 index 0000000000..0413aea0db --- /dev/null +++ b/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql @@ -0,0 +1,48 @@ + -- SPDX-License-Identifier: Apache-2.0 +-- Licensed to the Ed-Fi Alliance under one or more agreements. +-- The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +-- See the LICENSE and NOTICES files in the project root for more information. + +CREATE OR ALTER PROCEDURE dbo.CreateClientAccessToken( + @Id UNIQUEIDENTIFIER = NULL, + @Expiration DATETIME = NULL, + @Scope NVARCHAR(max) = NULL, + @ApiClientId INT = NULL, + @MaxTokenCount INT = NULL +) +AS +BEGIN + SET NOCOUNT ON + + DECLARE @ActiveTokenCount INT + DECLARE @ClientIsApproved INT + + SET @ClientIsApproved = (SELECT COUNT(1) + FROM dbo.ApiClients ac + WHERE ac.ApiClientId = @ApiClientId + AND ac.IsApproved = 1) + + IF (@ClientIsApproved = 0) + BEGIN + THROW 50000, 'Client is not approved', 1; + END + + IF @MaxTokenCount < 1 + SET @ActiveTokenCount = 0 + ELSE + BEGIN + SET @ActiveTokenCount = (SELECT COUNT(1) + FROM dbo.ClientAccessTokens actoken + WHERE ApiClient_ApiClientId = @ApiClientId + AND actoken.Expiration > GETUTCDATE()) + END + + IF (@MaxTokenCount < 1) OR (@ActiveTokenCount < @MaxTokenCount) + BEGIN + INSERT INTO dbo.ClientAccessTokens(Id, Expiration, Scope, ApiClient_ApiClientId) + VALUES (@Id, @Expiration, @Scope, @ApiClientId) + END + ELSE + THROW 50000, 'Token limit reached', 1; +END +GO diff --git a/Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql b/Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql new file mode 100644 index 0000000000..4e80d240e5 --- /dev/null +++ b/Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql @@ -0,0 +1,48 @@ +-- SPDX-License-Identifier: Apache-2.0 +-- Licensed to the Ed-Fi Alliance under one or more agreements. +-- The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +-- See the LICENSE and NOTICES files in the project root for more information. + +CREATE OR REPLACE PROCEDURE dbo.CreateClientAccessToken( + id uuid, + expiration timestamp without time zone, + scope text, + apiclientid integer, + maxtokencount integer) +AS +$BODY$ +DECLARE + active_token_count integer; + client_is_approved integer; +BEGIN + + SELECT count(1) + INTO client_is_approved + FROM dbo.apiclients ac + WHERE ac.apiclientid = createclientaccesstoken.ApiClientId + AND ac.isapproved = true; + + IF (client_is_approved = 0) THEN + RAISE EXCEPTION USING MESSAGE = 'Client is not arrpoved'; + END IF; + + IF maxtokencount < 1 THEN + active_token_count := 0; + ELSE + active_token_count := (SELECT COUNT(1) + FROM dbo.clientaccesstokens actoken + WHERE apiclient_apiclientid = ApiClientId + AND actoken.expiration > current_timestamp at time zone 'utc'); + END IF; + + IF (maxtokencount < 1) OR (active_token_count < maxtokencount) THEN + INSERT INTO dbo.ClientAccessTokens(id, expiration, scope, apiclient_apiclientid) + VALUES (CreateClientAccessToken.id, CreateClientAccessToken.expiration, CreateClientAccessToken.scope, + apiclientid); + ELSE + RAISE EXCEPTION USING MESSAGE = 'Token limit reached'; + END IF; + +END +$BODY$ + LANGUAGE plpgsql; From acc2125618ef553d2afa25630155e00998c929c9 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 09:51:51 -0500 Subject: [PATCH 02/13] Handle client not approved db exception --- .../Middleware/AuthenticationFailureMessages.cs | 1 + .../Security/Authentication/EdFiAdminAccessTokenFactory.cs | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs b/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs index 8b076a956f..29b78b4dde 100644 --- a/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs +++ b/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs @@ -11,4 +11,5 @@ public class AuthenticationFailureMessages public const string MissingAuthorizationHeaderBearerTokenValue = "Missing Authorization header bearer token value."; public const string MissingAuthorizationHeader = "Authorization header is missing."; public const string InvalidAuthorizationHeader = "Invalid Authorization header."; + public const string ClientNotApproved = "Client is not approved."; } diff --git a/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs b/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs index c10e1f47a3..7106d602e8 100644 --- a/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs +++ b/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs @@ -9,7 +9,9 @@ using System.Threading.Tasks; using Dapper; using EdFi.Admin.DataAccess.Providers; +using EdFi.Ods.Api.Middleware; using EdFi.Ods.Common.Exceptions; +using Microsoft.AspNetCore.Authentication; namespace EdFi.Ods.Api.Security.Authentication; @@ -22,6 +24,7 @@ public class EdFiAdminAccessTokenFactory : IAccessTokenFactory private const string AddTokenProcedureName = "dbo.CreateClientAccessToken"; private const string TokenLimitReachedDbMessage = "Token limit reached"; + private const string ClientNotApprovedDbMessage = "Client is not approved"; public EdFiAdminAccessTokenFactory( DbProviderFactory dbProviderFactory, @@ -60,6 +63,10 @@ public async Task CreateAccessTokenAsync(int apiClientId, string sc { await connection.ExecuteAsync(AddTokenProcedureName, @params, commandType: CommandType.StoredProcedure); } + catch (DbException ex) when (ex.Message.Contains(ClientNotApprovedDbMessage)) + { + throw new SecurityAuthenticationException(AuthenticationFailureMessages.ClientNotApproved); + } catch (DbException ex) when (ex.Message.Contains(TokenLimitReachedDbMessage)) { throw new TooManyTokensException(_tokenPerClientLimit); From cff31ae4bd3ccb624c6a5744df0f88743c616889 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 10:10:07 -0500 Subject: [PATCH 03/13] Update error wording to invalid client --- .../EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs | 2 +- .../Security/Authentication/EdFiAdminAccessTokenFactory.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs b/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs index 29b78b4dde..07a7aef273 100644 --- a/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs +++ b/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs @@ -11,5 +11,5 @@ public class AuthenticationFailureMessages public const string MissingAuthorizationHeaderBearerTokenValue = "Missing Authorization header bearer token value."; public const string MissingAuthorizationHeader = "Authorization header is missing."; public const string InvalidAuthorizationHeader = "Invalid Authorization header."; - public const string ClientNotApproved = "Client is not approved."; + public const string InvalidClient = "Invalid client."; } diff --git a/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs b/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs index 7106d602e8..bd788e6563 100644 --- a/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs +++ b/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs @@ -65,7 +65,7 @@ public async Task CreateAccessTokenAsync(int apiClientId, string sc } catch (DbException ex) when (ex.Message.Contains(ClientNotApprovedDbMessage)) { - throw new SecurityAuthenticationException(AuthenticationFailureMessages.ClientNotApproved); + throw new SecurityAuthenticationException(AuthenticationFailureMessages.InvalidClient); } catch (DbException ex) when (ex.Message.Contains(TokenLimitReachedDbMessage)) { From 11e2f32c5525f489e7cffe23eb79a294e2876f46 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 17:49:06 -0500 Subject: [PATCH 04/13] Align error response with OAuth spec --- .../EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs | 1 - .../Providers/ClientCredentialsTokenRequestProvider.cs | 5 +++++ .../Security/Authentication/EdFiAdminAccessTokenFactory.cs | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs b/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs index 07a7aef273..8b076a956f 100644 --- a/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs +++ b/Application/EdFi.Ods.Api/Middleware/AuthenticationFailureMessages.cs @@ -11,5 +11,4 @@ public class AuthenticationFailureMessages public const string MissingAuthorizationHeaderBearerTokenValue = "Missing Authorization header bearer token value."; public const string MissingAuthorizationHeader = "Authorization header is missing."; public const string InvalidAuthorizationHeader = "Invalid Authorization header."; - public const string InvalidClient = "Invalid client."; } diff --git a/Application/EdFi.Ods.Api/Providers/ClientCredentialsTokenRequestProvider.cs b/Application/EdFi.Ods.Api/Providers/ClientCredentialsTokenRequestProvider.cs index 53ab9d7a5a..2ea3322336 100644 --- a/Application/EdFi.Ods.Api/Providers/ClientCredentialsTokenRequestProvider.cs +++ b/Application/EdFi.Ods.Api/Providers/ClientCredentialsTokenRequestProvider.cs @@ -82,6 +82,11 @@ public async Task HandleAsync(TokenRequest tokenRequest) // create a new token var token = await _accessTokenFactory.CreateAccessTokenAsync(authenticationResult.ApiClientDetails.ApiClientId, tokenRequestScope); + + if (token == null) + { + return new AuthenticationResponse {TokenError = new TokenError(TokenErrorType.InvalidClient)}; + } var tokenResponse = new TokenResponse(); tokenResponse.SetToken(token.Id, (int) token.Duration.TotalSeconds, token.Scope); diff --git a/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs b/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs index bd788e6563..04670a3d4e 100644 --- a/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs +++ b/Application/EdFi.Ods.Api/Security/Authentication/EdFiAdminAccessTokenFactory.cs @@ -65,7 +65,7 @@ public async Task CreateAccessTokenAsync(int apiClientId, string sc } catch (DbException ex) when (ex.Message.Contains(ClientNotApprovedDbMessage)) { - throw new SecurityAuthenticationException(AuthenticationFailureMessages.InvalidClient); + return null; } catch (DbException ex) when (ex.Message.Contains(TokenLimitReachedDbMessage)) { From 72b3e66a1f61aef3f158f6a6084dde8595f6a561 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 19:48:56 -0500 Subject: [PATCH 05/13] Fix typo --- .../Structure/Admin/0176-Update-CreateClientAccessToken.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql b/Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql index 4e80d240e5..df1fa810cd 100644 --- a/Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql +++ b/Artifacts/PgSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql @@ -23,7 +23,7 @@ BEGIN AND ac.isapproved = true; IF (client_is_approved = 0) THEN - RAISE EXCEPTION USING MESSAGE = 'Client is not arrpoved'; + RAISE EXCEPTION USING MESSAGE = 'Client is not approved'; END IF; IF maxtokencount < 1 THEN From 2b2e2411371eda60fbadf01e3a418ef8aa24b460 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 21:04:12 -0500 Subject: [PATCH 06/13] Add optional IsApproved parameter when creating a client --- .../EdFi.Admin.DataAccess/Models/AccountModels.cs | 5 +++-- .../EdFi.Admin.DataAccess/Models/ApiClient.cs | 2 +- .../Repositories/ClientAppRepo.cs | 12 ++++++------ .../Repositories/IClientAppRepo.cs | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Application/EdFi.Admin.DataAccess/Models/AccountModels.cs b/Application/EdFi.Admin.DataAccess/Models/AccountModels.cs index 12e820c7ec..5d5d8e373c 100644 --- a/Application/EdFi.Admin.DataAccess/Models/AccountModels.cs +++ b/Application/EdFi.Admin.DataAccess/Models/AccountModels.cs @@ -36,13 +36,14 @@ public User() /// Empty, Minimal, Populated /// optional parameter, value is created randomly if it is null or empty. Both Key and Secret are required if providing either. /// optional parameter, value is created randomly if it is null or empty. Both Key and Secret are required if providing either. + /// optional parameter, is this client allowed to authenticate. Default is true. /// ApiClient information about the created sandbox - public ApiClient AddSandboxClient(string name, SandboxType sandboxType, string key, string secret) + public ApiClient AddSandboxClient(string name, SandboxType sandboxType, string key, string secret, bool isApproved = true) { var client = new ApiClient(true) { Name = name, - IsApproved = true, + IsApproved = isApproved, UseSandbox = true, SandboxType = sandboxType }; diff --git a/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs b/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs index 0089e6d3df..96d463f925 100644 --- a/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs +++ b/Application/EdFi.Admin.DataAccess/Models/ApiClient.cs @@ -75,7 +75,7 @@ public ApiClient(bool generateKey = false) public string Name { get; set; } /// - /// Lock-out the application if not approved (Auto-approve in sandbox) + /// Lock-out the application if not approved /// public bool IsApproved { get; set; } diff --git a/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs b/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs index 258b2f3149..8e2135c61d 100644 --- a/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs +++ b/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs @@ -439,11 +439,11 @@ public void AddApiClientToUserWithVendorApplication(int userId, ApiClient client } } - public ApiClient CreateApiClient(int userId, string name, string key, string secret) + public ApiClient CreateApiClient(int userId, string name, string key, string secret, bool isApproved) { using (var context = _contextFactory.CreateContext()) { - var client = CreateApiClient(context, userId, name, SandboxType.Sample, key, secret); + var client = CreateApiClient(context, userId, name, SandboxType.Sample, key, secret, isApproved); context.SaveChanges(); @@ -723,17 +723,17 @@ private OwnershipToken GetOrCreateOwnershipToken(string ownershipToken) } } - private ApiClient CreateApiClient( - IUsersContext context, + private ApiClient CreateApiClient(IUsersContext context, int userId, string name, SandboxType sandboxType, string key, - string secret) + string secret, + bool isApproved = true) { var attachedUser = context.Users.Find(userId); - return attachedUser.AddSandboxClient(name, sandboxType, key, secret); + return attachedUser.AddSandboxClient(name, sandboxType, key, secret, isApproved); } private void AddApplicationEducationOrganizations(IUsersContext context, int applicationId, ApiClient client) diff --git a/Application/EdFi.Admin.DataAccess/Repositories/IClientAppRepo.cs b/Application/EdFi.Admin.DataAccess/Repositories/IClientAppRepo.cs index 5b3540b2e6..67b1a559ed 100644 --- a/Application/EdFi.Admin.DataAccess/Repositories/IClientAppRepo.cs +++ b/Application/EdFi.Admin.DataAccess/Repositories/IClientAppRepo.cs @@ -68,7 +68,7 @@ void SetupKeySecret(string name, SandboxType sandboxType, string key, string sec Application CreateApplicationForVendor(int vendorId, string applicationName, string claimSetName); - ApiClient CreateApiClient(int userId, string name, string key, string secret); + ApiClient CreateApiClient(int userId, string name, string key, string secret, bool isApproved = true); void AddEdOrgIdsToApiClient(int userId, int apiClientId, IList edOrgIds, int applicationId); From 6ee00dc6a4da827b3bea777f1af2d6843928ffff Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 21:45:40 -0500 Subject: [PATCH 07/13] Update Admin.DataAccess library version --- Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj | 2 +- Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj | 2 +- Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj | 2 +- .../EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj | 2 +- .../EdFi.Ods.WebApi.IntegrationTests.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj b/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj index fbbfee15d7..35302fbe0e 100644 --- a/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj +++ b/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj @@ -26,7 +26,7 @@ - + diff --git a/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj b/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj index 0a7d1d1d04..a49e95bc0a 100644 --- a/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj +++ b/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj @@ -20,7 +20,7 @@ - + diff --git a/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj b/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj index ad438d4ed4..17c04e89cc 100644 --- a/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj +++ b/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj b/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj index db8a9b1deb..871ce4022d 100644 --- a/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj +++ b/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj @@ -32,7 +32,7 @@ - + diff --git a/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj b/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj index 35b6b41ff7..05ca30d202 100644 --- a/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj +++ b/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj @@ -21,7 +21,7 @@ - + From e70daab6db2d66868352c21d759b93fb2ef1ad05 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Wed, 23 Oct 2024 23:09:39 -0500 Subject: [PATCH 08/13] Add Postman test for unapproved API client authorization attempt --- ...gration Test Suite.postman_collection.json | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json b/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json index f082086f82..c06cb3e779 100644 --- a/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json +++ b/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json @@ -1090,6 +1090,97 @@ } }, "response": [] + }, + { + "name": "Unapproved Client With Correct Key/Secret Request", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const clientSuffix = \"255901\";\r", + "\r", + "const apiKeyName = `ApiKey_${clientSuffix}`;\r", + "const apiSecretName = `ApiSecret_${clientSuffix}`;\r", + "\r", + "let clientId = pm.environment.get(apiKeyName) ?? clientSuffix;\r", + "let clientSecret = pm.environment.get(apiSecretName) ?? clientSuffix;\r", + "\r", + "pm.environment.set(`known:${apiKeyName}`, clientId);\r", + "pm.environment.set(`known:${apiSecretName}`, clientSecret);\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 401\", () => {\r", + " pm.expect(pm.response.code).to.equal(401);\r", + "});\r", + "\r", + "pm.test(\"Should return a invalid_client\", () => {\r", + " const responseItem = pm.response.json();\r", + " pm.expect(responseItem.error).to.equal(\"invalid_client\");\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "disabledSystemHeaders": {} + }, + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "{{ApiSecret_Unapproved_Client}}", + "type": "string" + }, + { + "key": "username", + "value": "{{ApiKey_Unapproved_Client}}", + "type": "string" + } + ] + }, + "method": "POST", + "header": [], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "mode", + "value": "urlencoded", + "type": "default" + }, + { + "key": "grant_type", + "value": "client_credentials", + "type": "default" + } + ] + }, + "url": { + "raw": "{{ApiBaseUrl}}/oauth/token", + "host": [ + "{{ApiBaseUrl}}" + ], + "path": [ + "oauth", + "token" + ] + } + }, + "response": [] } ] }, From 7c6bda437fc6ce1142752532e95521cefea803b5 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Thu, 24 Oct 2024 09:28:29 -0500 Subject: [PATCH 09/13] Formatting --- .../Structure/Admin/0176-Update-CreateClientAccessToken.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql b/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql index 0413aea0db..6e48766393 100644 --- a/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql +++ b/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql @@ -1,4 +1,4 @@ - -- SPDX-License-Identifier: Apache-2.0 +-- SPDX-License-Identifier: Apache-2.0 -- Licensed to the Ed-Fi Alliance under one or more agreements. -- The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. -- See the LICENSE and NOTICES files in the project root for more information. From 6bfff97c280955e69eeccf49354602ed4543de0d Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Thu, 24 Oct 2024 09:37:49 -0500 Subject: [PATCH 10/13] Update Postman test --- ...i ODS-API Integration Test Suite.postman_collection.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json b/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json index c06cb3e779..7711d29f11 100644 --- a/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json +++ b/Postman Test Suite/Ed-Fi ODS-API Integration Test Suite.postman_collection.json @@ -1098,7 +1098,7 @@ "listen": "prerequest", "script": { "exec": [ - "const clientSuffix = \"255901\";\r", + "const clientSuffix = \"Unapproved_Client\";\r", "\r", "const apiKeyName = `ApiKey_${clientSuffix}`;\r", "const apiSecretName = `ApiSecret_${clientSuffix}`;\r", @@ -1142,12 +1142,12 @@ "basic": [ { "key": "password", - "value": "{{ApiSecret_Unapproved_Client}}", + "value": "{{known:ApiSecret_Unapproved_Client}}", "type": "string" }, { "key": "username", - "value": "{{ApiKey_Unapproved_Client}}", + "value": "{{known:ApiKey_Unapproved_Client}}", "type": "string" } ] From b642e0af2a9f4d7977d8b00514be792eaf3ee9dd Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Thu, 24 Oct 2024 10:41:49 -0500 Subject: [PATCH 11/13] Formatting --- .../0176-Update-CreateClientAccessToken.sql | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql b/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql index 6e48766393..bdfe0bb929 100644 --- a/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql +++ b/Artifacts/MsSql/Structure/Admin/0176-Update-CreateClientAccessToken.sql @@ -16,33 +16,33 @@ BEGIN DECLARE @ActiveTokenCount INT DECLARE @ClientIsApproved INT - + SET @ClientIsApproved = (SELECT COUNT(1) - FROM dbo.ApiClients ac - WHERE ac.ApiClientId = @ApiClientId - AND ac.IsApproved = 1) - + FROM dbo.ApiClients ac + WHERE ac.ApiClientId = @ApiClientId + AND ac.IsApproved = 1) + IF (@ClientIsApproved = 0) BEGIN THROW 50000, 'Client is not approved', 1; END - - IF @MaxTokenCount < 1 - SET @ActiveTokenCount = 0 - ELSE - BEGIN + + IF @MaxTokenCount < 1 + SET @ActiveTokenCount = 0 + ELSE + BEGIN SET @ActiveTokenCount = (SELECT COUNT(1) - FROM dbo.ClientAccessTokens actoken - WHERE ApiClient_ApiClientId = @ApiClientId - AND actoken.Expiration > GETUTCDATE()) - END - + FROM dbo.ClientAccessTokens actoken + WHERE ApiClient_ApiClientId = @ApiClientId + AND actoken.Expiration > GETUTCDATE()) + END + IF (@MaxTokenCount < 1) OR (@ActiveTokenCount < @MaxTokenCount) - BEGIN - INSERT INTO dbo.ClientAccessTokens(Id, Expiration, Scope, ApiClient_ApiClientId) - VALUES (@Id, @Expiration, @Scope, @ApiClientId) - END - ELSE - THROW 50000, 'Token limit reached', 1; + BEGIN + INSERT INTO dbo.ClientAccessTokens(Id, Expiration, Scope, ApiClient_ApiClientId) + VALUES (@Id, @Expiration, @Scope, @ApiClientId) + END + ELSE + THROW 50000, 'Token limit reached', 1; END GO From 11754a38343052fdac04ee86cf842a008264b852 Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Thu, 24 Oct 2024 15:09:21 -0500 Subject: [PATCH 12/13] Change IsApproved paramter on private method to non-optional --- .../EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs b/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs index 8e2135c61d..f07a8c1831 100644 --- a/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs +++ b/Application/EdFi.Admin.DataAccess/Repositories/ClientAppRepo.cs @@ -461,7 +461,7 @@ public void SetupKeySecret( { using (var context = _contextFactory.CreateContext()) { - var client = CreateApiClient(context, userId, name, sandboxType, key, secret); + var client = CreateApiClient(context, userId, name, sandboxType, key, secret, true); AddApplicationEducationOrganizations(context, applicationId, client); @@ -509,7 +509,7 @@ public ApiClient SetupDefaultSandboxClient( using (var context = _contextFactory.CreateContext()) { _logger.Debug($"Creating API Client"); - var client = GetClient(key, secret) ?? CreateApiClient(context, userId, name, sandboxType, key, secret); + var client = GetClient(key, secret) ?? CreateApiClient(context, userId, name, sandboxType, key, secret, true); _logger.Debug($"Adding Education Organization to client"); AddApplicationEducationOrganizations(context, applicationId, client); @@ -728,8 +728,8 @@ private ApiClient CreateApiClient(IUsersContext context, string name, SandboxType sandboxType, string key, - string secret, - bool isApproved = true) + string secret, + bool isApproved) { var attachedUser = context.Users.Find(userId); From 97db179d2f3e061daa39d87cf308b7ed17c29aec Mon Sep 17 00:00:00 2001 From: Matthew Jackson Date: Thu, 24 Oct 2024 15:13:33 -0500 Subject: [PATCH 13/13] Update Admin.DataAccess library version --- Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj | 2 +- Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj | 2 +- Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj | 2 +- .../EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj | 2 +- .../EdFi.Ods.WebApi.IntegrationTests.csproj | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj b/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj index 35302fbe0e..1cc6d07b9f 100644 --- a/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj +++ b/Application/EdFi.Ods.Api/EdFi.Ods.Api.csproj @@ -26,7 +26,7 @@ - + diff --git a/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj b/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj index a49e95bc0a..9d41137f36 100644 --- a/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj +++ b/Application/EdFi.Ods.Sandbox/EdFi.Ods.Sandbox.csproj @@ -20,7 +20,7 @@ - + diff --git a/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj b/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj index 17c04e89cc..4e2c8e977b 100644 --- a/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj +++ b/Application/EdFi.Ods.Tests/EdFi.Ods.Tests.csproj @@ -27,7 +27,7 @@ - + diff --git a/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj b/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj index 871ce4022d..e2b6b3b7b1 100644 --- a/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj +++ b/tests/EdFi.Ods.WebApi.CompositeSpecFlowTests/EdFi.Ods.WebApi.CompositeSpecFlowTests.csproj @@ -32,7 +32,7 @@ - + diff --git a/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj b/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj index 05ca30d202..883806ab43 100644 --- a/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj +++ b/tests/EdFi.Ods.WebApi.IntegrationTests/EdFi.Ods.WebApi.IntegrationTests.csproj @@ -21,7 +21,7 @@ - +