Skip to content

Commit 2871179

Browse files
authored
Merge pull request #25 from umbraco/feature/manually-add-api-key
Feature/manually add api key
2 parents 6b83afd + 34d7e7f commit 2871179

28 files changed

+398
-51
lines changed

src/Umbraco.AuthorizedServices/AuthorizedServicesComposer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ private static void RegisterServices(IUmbracoBuilder builder)
5757

5858
builder.Services.AddUnique<ITokenFactory, TokenFactory>();
5959
builder.Services.AddUnique<ITokenStorage, DatabaseTokenStorage>();
60+
builder.Services.AddUnique<IKeyStorage, DatabaseKeyStorage>();
6061

6162
builder.Services.AddSingleton<JsonSerializerFactory>();
6263
}

src/Umbraco.AuthorizedServices/ClientApp/src/backoffice/AuthorizedServices/edit.controller.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,12 @@ function AuthorizedServiceEditController(this: any, $routeParams, $location, aut
1414
vm.isAuthorized = serviceData.isAuthorized;
1515
vm.authenticationMethod = serviceData.authenticationMethod;
1616
vm.canManuallyProvideToken = serviceData.canManuallyProvideToken;
17+
vm.canManuallyProvideApiKey = serviceData.canManuallyProvideApiKey;
1718
vm.authorizationUrl = serviceData.authorizationUrl;
1819
vm.sampleRequest = serviceData.sampleRequest;
1920
vm.sampleRequestResponse = null;
2021
vm.settings = serviceData.settings;
21-
vm.isApiKeyBasedAuthenticationMethod = serviceData.authenticationMethod === AuthenticationMethod.ApiKey;
22+
vm.isOAuthBasedAuthenticationMethod = serviceData.authenticationMethod !== AuthenticationMethod.ApiKey;
2223
});
2324
}
2425

@@ -67,6 +68,19 @@ function AuthorizedServiceEditController(this: any, $routeParams, $location, aut
6768
}
6869
}
6970

71+
vm.saveApiKey = function () {
72+
let inApiKey = <HTMLInputElement>document.getElementById("inApiKey");
73+
74+
if (inApiKey) {
75+
authorizedServiceResource.saveApiKey(serviceAlias, inApiKey.value)
76+
.then(function () {
77+
notificationsService.success("Authorized Services", "The '" + vm.displayName + "' service API key has been saved.");
78+
inApiKey.value = "";
79+
loadServiceDetails(serviceAlias);
80+
});
81+
}
82+
}
83+
7084
loadServiceDetails(serviceAlias);
7185

7286
}

src/Umbraco.AuthorizedServices/ClientApp/src/backoffice/AuthorizedServices/edit.html

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@
2424
action="vm.sendSampleRequest()"
2525
type="button"
2626
label="Verify Sample Request"></umb-button>
27-
<umb-button ng-if="!vm.isApiKeyBasedAuthenticationMethod"
27+
<umb-button ng-if="vm.isOAuthBasedAuthenticationMethod
28+
|| (!vm.isOAuthBasedAuthenticationMethod && vm.canManuallyProvideApiKey)"
2829
action="vm.revokeAccess()"
2930
type="button"
3031
button-style="danger"
@@ -47,6 +48,7 @@
4748
</uui-icon-registry-essential>
4849
</umb-box-content>
4950
</umb-box>
51+
<!-- Provide Token Section -->
5052
<umb-box ng-if="vm.canManuallyProvideToken">
5153
<umb-box-header title="Provide Token"></umb-box-header>
5254
<umb-box-content>
@@ -66,6 +68,29 @@
6668
</uui-icon-registry-essential>
6769
</umb-box-content>
6870
</umb-box>
71+
<!-- Provide Key Section -->
72+
<umb-box ng-if="vm.canManuallyProvideApiKey">
73+
<umb-box-header title="Provide API key"></umb-box-header>
74+
<umb-box-content>
75+
<uui-icon-registry-essential>
76+
<uui-card-content-node name="API Key">
77+
<uui-icon slot="icon" name="add"></uui-icon>
78+
<p class="auth-srv">Enter service API key</p>
79+
<p>
80+
This service is configured indicating that an API key can be created via the service's developer portal.
81+
Once you have obtained one you can copy and paste it here to authorize the service.
82+
</p>
83+
<div>
84+
<uui-input id="inApiKey" type="text" name="inApiKey" style="width: 40%;font-size:14px;"></uui-input>
85+
<umb-button action="vm.saveApiKey()"
86+
type="button"
87+
button-style="primary"
88+
label="Save"></umb-button>
89+
</div>
90+
</uui-card-content-node>
91+
</uui-icon-registry-essential>
92+
</umb-box-content>
93+
</umb-box>
6994
<umb-box>
7095
<umb-box-header title="Settings"></umb-box-header>
7196
<umb-box-content>

src/Umbraco.AuthorizedServices/ClientApp/src/resources/authorizedservice.resource.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ function authorizedServiceResource($q, $http) {
1717
saveToken: function (alias: string, token: string) {
1818
return $http.post(apiRoot + "SaveToken", { alias: alias, token: token });
1919
},
20+
saveApiKey: function (alias: string, apiKey: string) {
21+
return $http.post(apiRoot + "SaveApiKey", { alias: alias, apiKey: apiKey });
22+
},
2023
generateToken: function (alias: string) {
2124
return $http.post(apiRoot + "GenerateToken", { alias: alias });
2225
}

src/Umbraco.AuthorizedServices/Configuration/AuthorizedServiceSettings.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,15 @@ public class ServiceDetail : ServiceSummary
133133
public string TokenHost { get; set; } = string.Empty;
134134

135135
/// <summary>
136-
/// Get or sets a value indicating whether an adminsitrator editor can manually provide tokens via the backoffice.
136+
/// Get or sets a value indicating whether an administrator can manually provide tokens via the backoffice.
137137
/// </summary>
138138
public bool CanManuallyProvideToken { get; set; }
139139

140+
/// <summary>
141+
/// Get or sets a value indicating whether an administrator can manually provide API keys via the backoffice.
142+
/// </summary>
143+
public bool CanManuallyProvideApiKey { get; set; }
144+
140145
/// <summary>
141146
/// Gets or sets the path for requests for authentication with the service.
142147
/// </summary>

src/Umbraco.AuthorizedServices/Constants.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,14 @@ public static class Trees
4444

4545
public static class Migrations
4646
{
47-
public const string TableName = "umbracoAuthorizedServiceToken";
47+
public const string UmbracoAuthorizedServiceTokenTableName = "umbracoAuthorizedServiceToken";
48+
49+
public const string UmbracoAuthorizedServiceKeyTableName = "umbracoAuthorizedServiceKey";
4850

4951
public const string MigrationPlan = "AuthorizedServicesDatabaseMigration";
5052

51-
public const string TargetState = "authorizedServices-db";
53+
public const string UmbracoAuthorizedServiceTokenTargetState = "authorizedServices-token-db";
54+
55+
public const string UmbracoAuthorizedServiceKeyTargetState = "authorizedServices-key-db";
5256
}
5357
}

src/Umbraco.AuthorizedServices/Controllers/AuthorizedServiceController.cs

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class AuthorizedServiceController : BackOfficeNotificationsController
2020
{
2121
private readonly IOptionsMonitor<ServiceDetail> _serviceDetailOptions;
2222
private readonly ITokenStorage _tokenStorage;
23+
private readonly IKeyStorage _keyStorage;
2324
private readonly IAuthorizationUrlBuilder _authorizationUrlBuilder;
2425
private readonly IAuthorizedServiceCaller _authorizedServiceCaller;
2526
private readonly IAuthorizationPayloadCache _authorizedServiceAuthorizationPayloadCache;
@@ -32,6 +33,7 @@ public class AuthorizedServiceController : BackOfficeNotificationsController
3233
public AuthorizedServiceController(
3334
IOptionsMonitor<ServiceDetail> serviceDetailOptions,
3435
ITokenStorage tokenStorage,
36+
IKeyStorage keyStorage,
3537
IAuthorizationUrlBuilder authorizationUrlBuilder,
3638
IAuthorizedServiceCaller authorizedServiceCaller,
3739
IAuthorizationPayloadCache authorizedServiceAuthorizationPayloadCache,
@@ -40,6 +42,7 @@ public AuthorizedServiceController(
4042
{
4143
_serviceDetailOptions = serviceDetailOptions;
4244
_tokenStorage = tokenStorage;
45+
_keyStorage = keyStorage;
4346
_authorizationUrlBuilder = authorizationUrlBuilder;
4447
_authorizedServiceCaller = authorizedServiceCaller;
4548
_authorizedServiceAuthorizationPayloadCache = authorizedServiceAuthorizationPayloadCache;
@@ -77,6 +80,7 @@ public AuthorizedServiceController(
7780
DisplayName = serviceDetail.DisplayName,
7881
IsAuthorized = isAuthorized,
7982
CanManuallyProvideToken = serviceDetail.CanManuallyProvideToken,
83+
CanManuallyProvideApiKey = serviceDetail.CanManuallyProvideApiKey,
8084
AuthorizationUrl = authorizationUrl,
8185
AuthenticationMethod = serviceDetail.AuthenticationMethod.ToString(),
8286
SampleRequest = serviceDetail.SampleRequest,
@@ -120,13 +124,21 @@ public async Task<IActionResult> SendSampleRequest(string alias, string path)
120124
}
121125

122126
/// <summary>
123-
/// Revokes access by removing the access token for an authorized service.
127+
/// Revokes access by removing the access token or API key for an authorized service.
124128
/// </summary>
125129
/// <param name="model">Request model identifying the service.</param>
126130
[HttpPost]
127131
public IActionResult RevokeAccess(RevokeAccess model)
128132
{
129-
_tokenStorage.DeleteToken(model.Alias);
133+
ServiceDetail serviceDetail = _serviceDetailOptions.Get(model.Alias);
134+
if (serviceDetail.AuthenticationMethod != AuthenticationMethod.ApiKey)
135+
{
136+
_tokenStorage.DeleteToken(model.Alias);
137+
}
138+
else
139+
{
140+
_keyStorage.DeleteKey(model.Alias);
141+
}
130142
return Ok();
131143
}
132144

@@ -142,6 +154,18 @@ public IActionResult SaveToken(AddToken model)
142154
return Ok();
143155
}
144156

157+
/// <summary>
158+
/// Adds a new API key for an authorized service.
159+
/// </summary>
160+
/// <param name="model">Request model identifying the service.</param>
161+
/// <returns></returns>
162+
[HttpPost]
163+
public IActionResult SaveApiKey(AddApiKey model)
164+
{
165+
_keyStorage.SaveKey(model.Alias, model.ApiKey);
166+
return Ok();
167+
}
168+
145169
/// <summary>
146170
/// Generates access token for an authorized service.
147171
/// </summary>
@@ -168,7 +192,8 @@ public async Task<IActionResult> GenerateToken(GenerateToken model)
168192
AuthenticationMethod.OAuth1 => false,
169193
AuthenticationMethod.OAuth2AuthorizationCode => StoredTokenExists(serviceDetail),
170194
AuthenticationMethod.OAuth2ClientCredentials => StoredTokenExists(serviceDetail),
171-
AuthenticationMethod.ApiKey => !string.IsNullOrEmpty(serviceDetail.ApiKey),
195+
AuthenticationMethod.ApiKey => !string.IsNullOrEmpty(serviceDetail.ApiKey)
196+
|| _keyStorage.GetKey(serviceDetail.Alias) is not null,
172197
_ => false
173198
};
174199

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Microsoft.Extensions.Logging;
2+
using Umbraco.AuthorizedServices.Persistence.Dtos;
3+
using Umbraco.Cms.Infrastructure.Migrations;
4+
5+
namespace Umbraco.AuthorizedServices.Migrations;
6+
7+
public class AddDatabaseKeyStorageTable : MigrationBase
8+
{
9+
public AddDatabaseKeyStorageTable(IMigrationContext context) : base(context)
10+
{
11+
}
12+
13+
protected override void Migrate()
14+
{
15+
Logger.LogDebug($"Running migration {nameof(AddDatabaseKeyStorageTable)}");
16+
17+
if (TableExists(Constants.Migrations.UmbracoAuthorizedServiceKeyTableName))
18+
{
19+
Logger.LogDebug($"The database table {Constants.Migrations.UmbracoAuthorizedServiceKeyTableName} already exists, skipping.");
20+
}
21+
else
22+
{
23+
Create.Table<KeyDto>().Do();
24+
}
25+
}
26+
}

src/Umbraco.AuthorizedServices/Migrations/AddDatabaseTokenStorageTable.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ protected override void Migrate()
1414
{
1515
Logger.LogDebug($"Running migration {nameof(AddDatabaseTokenStorageTable)}");
1616

17-
if (TableExists(Constants.Migrations.TableName))
17+
if (TableExists(Constants.Migrations.UmbracoAuthorizedServiceTokenTableName))
1818
{
19-
Logger.LogDebug($"The database table {Constants.Migrations.TableName} already exists, skipping.");
19+
Logger.LogDebug($"The database table {Constants.Migrations.UmbracoAuthorizedServiceTokenTableName} already exists, skipping.");
2020
}
2121
else
2222
{

src/Umbraco.AuthorizedServices/Migrations/AuthorizedServicesMigrationPlan.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public AuthorizedServicesMigrationPlan()
2020
/// <inheritdoc />
2121
protected override void DefinePlan()
2222
{
23-
To<AddDatabaseTokenStorageTable>(Constants.Migrations.TargetState);
23+
To<AddDatabaseTokenStorageTable>(Constants.Migrations.UmbracoAuthorizedServiceTokenTargetState);
24+
To<AddDatabaseKeyStorageTable>(Constants.Migrations.UmbracoAuthorizedServiceKeyTargetState);
2425
}
2526
}

0 commit comments

Comments
 (0)