From 5914434cfdd163fad8d30ca879577e07b4009aa3 Mon Sep 17 00:00:00 2001 From: Marcial Rosales Date: Fri, 12 May 2023 16:03:45 +0200 Subject: [PATCH] Support OAuth2 authentication Change test assertion Restore original test The new test did work in Unix but failed on Windows Fix whitespace, remove use of #nullable, do not remove UserName / Password from connection factory Do not make unnecessary API changes Add tls-gen as a submodule, start getting bash scripts to pass shellcheck Bash script refactoring Use full docker options Clean up OAuth2 test Program Remove unused code in start-rabbitmq.sh Start to move OAuth2 code to its own project Finish moving OAuth2 code to its own project Remove OAuth2 from APIApproval Add OAuth2 API approval test and move verified output into each directory Version the RabbitMQ.Client.OAuth2 project independently of the RabbitMQ.Client project Fix lack of whitespaces, use TimeSpan to represent a duration rather than a long. Fix APIApproval Fix project refs, use forward slashes --- Build.csproj | 1 + RabbitMQDotNetClient.sln | 10 +- projects/Benchmarks/Benchmarks.csproj | 2 +- .../RabbitMQ.Client.OAuth2/OAuth2Client.cs | 296 ++ .../OAuth2CredentialsProvider.cs | 142 + projects/RabbitMQ.Client.OAuth2/README.md | 1 + .../RabbitMQ.Client.OAuth2.csproj | 72 + projects/RabbitMQ.Client.OAuth2/icon.png | Bin 0 -> 6766 bytes projects/RabbitMQ.Client/README.md | 1 + .../RabbitMQ.Client/RabbitMQ.Client.csproj | 4 +- .../client/api/BasicCredentialsProvider.cs | 76 + .../client/api/ConnectionConfig.cs | 16 +- .../client/api/ConnectionFactory.cs | 24 +- .../client/api/IConnectionFactory.cs | 8 + .../client/api/ICredentialsProvider.cs | 53 + .../client/api/ICredentialsRefresher.cs | 140 + .../client/api/PlainMechanism.cs | 2 +- .../client/impl/AutorecoveringConnection.cs | 3 - .../client/impl/Connection.Commands.cs | 18 + .../CreateChannel/CreateChannel.csproj | 2 +- .../MassPublish/MassPublish.csproj | 2 +- .../TestApplications/OAuth2/OAuth2.csproj | 22 + projects/TestApplications/OAuth2/Program.cs | 153 + projects/TestApplications/OAuth2/README.md | 33 + projects/TestApplications/OAuth2/common.sh | 20 + .../TestApplications/OAuth2/enabled_plugins | 1 + .../OAuth2/keycloak/import/test-realm.json | 2488 +++++++++++++++++ .../OAuth2/keycloak/rabbitmq.conf | 7 + .../keycloak/signing-key/signing-key.pem | 9 + .../TestApplications/OAuth2/start-keycloak.sh | 33 + .../TestApplications/OAuth2/start-rabbit.sh | 39 + .../TestApplications/OAuth2/start-test.sh | 26 + projects/TestApplications/OAuth2/start-uaa.sh | 27 + .../TestApplications/OAuth2/stop-keycloak.sh | 12 + .../TestApplications/OAuth2/stop-rabbit.sh | 12 + projects/TestApplications/OAuth2/stop-uaa.sh | 12 + .../OAuth2/uaa/log4j2.properties | 30 + .../TestApplications/OAuth2/uaa/rabbitmq.conf | 15 + .../OAuth2/uaa/signing-key/signing-key.pem | 9 + projects/TestApplications/OAuth2/uaa/uaa.yml | 171 ++ projects/Unit/APIApproval.cs | 29 +- projects/Unit/Fixtures.cs | 45 +- projects/Unit/Helper/RequestFormMatcher.cs | 63 + .../APIApproval.Approve.verified.txt | 50 + .../APIApproval.Approve.verified.txt | 52 + projects/Unit/RabbitMQCtl.cs | 19 + projects/Unit/TestOAuth2Client.cs | 168 ++ .../TestOAuth2ClientCredentialsProvider.cs | 123 + .../Unit/TestTimerBasedCredentialRefresher.cs | 97 + projects/Unit/TestUpdateSecret.cs | 38 +- projects/Unit/Unit.csproj | 10 +- 51 files changed, 4611 insertions(+), 75 deletions(-) create mode 100644 projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs create mode 100644 projects/RabbitMQ.Client.OAuth2/OAuth2CredentialsProvider.cs create mode 100644 projects/RabbitMQ.Client.OAuth2/README.md create mode 100644 projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj create mode 100644 projects/RabbitMQ.Client.OAuth2/icon.png create mode 100644 projects/RabbitMQ.Client/README.md create mode 100644 projects/RabbitMQ.Client/client/api/BasicCredentialsProvider.cs create mode 100644 projects/RabbitMQ.Client/client/api/ICredentialsProvider.cs create mode 100644 projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs create mode 100644 projects/TestApplications/OAuth2/OAuth2.csproj create mode 100644 projects/TestApplications/OAuth2/Program.cs create mode 100644 projects/TestApplications/OAuth2/README.md create mode 100755 projects/TestApplications/OAuth2/common.sh create mode 100644 projects/TestApplications/OAuth2/enabled_plugins create mode 100644 projects/TestApplications/OAuth2/keycloak/import/test-realm.json create mode 100644 projects/TestApplications/OAuth2/keycloak/rabbitmq.conf create mode 100644 projects/TestApplications/OAuth2/keycloak/signing-key/signing-key.pem create mode 100755 projects/TestApplications/OAuth2/start-keycloak.sh create mode 100755 projects/TestApplications/OAuth2/start-rabbit.sh create mode 100755 projects/TestApplications/OAuth2/start-test.sh create mode 100755 projects/TestApplications/OAuth2/start-uaa.sh create mode 100755 projects/TestApplications/OAuth2/stop-keycloak.sh create mode 100755 projects/TestApplications/OAuth2/stop-rabbit.sh create mode 100755 projects/TestApplications/OAuth2/stop-uaa.sh create mode 100644 projects/TestApplications/OAuth2/uaa/log4j2.properties create mode 100644 projects/TestApplications/OAuth2/uaa/rabbitmq.conf create mode 100644 projects/TestApplications/OAuth2/uaa/signing-key/signing-key.pem create mode 100644 projects/TestApplications/OAuth2/uaa/uaa.yml create mode 100644 projects/Unit/Helper/RequestFormMatcher.cs create mode 100644 projects/Unit/RabbitMQ.Client.OAuth2/APIApproval.Approve.verified.txt rename projects/Unit/{ => RabbitMQ.Client}/APIApproval.Approve.verified.txt (95%) create mode 100644 projects/Unit/TestOAuth2Client.cs create mode 100644 projects/Unit/TestOAuth2ClientCredentialsProvider.cs create mode 100644 projects/Unit/TestTimerBasedCredentialRefresher.cs diff --git a/Build.csproj b/Build.csproj index 2acee38279..01008f2878 100644 --- a/Build.csproj +++ b/Build.csproj @@ -8,6 +8,7 @@ + diff --git a/RabbitMQDotNetClient.sln b/RabbitMQDotNetClient.sln index ee53fa9c93..490df48bd0 100644 --- a/RabbitMQDotNetClient.sln +++ b/RabbitMQDotNetClient.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29806.167 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34003.232 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{34486CC0-D61E-46BA-9E5E-6E8EFA7C34B5}" ProjectSection(SolutionItems) = preProject @@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestApplications", "TestApp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CreateChannel", "projects\TestApplications\CreateChannel\CreateChannel.csproj", "{4A589408-F3A3-40E1-A6DF-F5E620F7CA31}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RabbitMQ.Client.OAuth2", "projects\RabbitMQ.Client.OAuth2\RabbitMQ.Client.OAuth2.csproj", "{794C7B31-0E9A-44A4-B285-0F3CAF6209F1}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -45,6 +47,10 @@ Global {4A589408-F3A3-40E1-A6DF-F5E620F7CA31}.Debug|Any CPU.Build.0 = Debug|Any CPU {4A589408-F3A3-40E1-A6DF-F5E620F7CA31}.Release|Any CPU.ActiveCfg = Release|Any CPU {4A589408-F3A3-40E1-A6DF-F5E620F7CA31}.Release|Any CPU.Build.0 = Release|Any CPU + {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {794C7B31-0E9A-44A4-B285-0F3CAF6209F1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/projects/Benchmarks/Benchmarks.csproj b/projects/Benchmarks/Benchmarks.csproj index d780f4e332..bb69066308 100644 --- a/projects/Benchmarks/Benchmarks.csproj +++ b/projects/Benchmarks/Benchmarks.csproj @@ -20,7 +20,7 @@ - + diff --git a/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs b/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs new file mode 100644 index 0000000000..9a8abb7ee5 --- /dev/null +++ b/projects/RabbitMQ.Client.OAuth2/OAuth2Client.cs @@ -0,0 +1,296 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace RabbitMQ.Client.OAuth2 +{ + public interface IOAuth2Client + { + public IToken RequestToken(); + public IToken RefreshToken(IToken token); + } + + public interface IToken + { + public string AccessToken { get; } + public string RefreshToken { get; } + public TimeSpan ExpiresIn { get; } + public bool hasExpired { get; } + } + + public class Token : IToken + { + private readonly JsonToken _source; + private readonly DateTime _lastTokenRenewal; + + public Token(JsonToken json) + { + this._source = json; + this._lastTokenRenewal = DateTime.Now; + } + + public string AccessToken + { + get + { + return _source.access_token; + } + } + + public string RefreshToken + { + get + { + return _source.refresh_token; + } + } + + public TimeSpan ExpiresIn + { + get + { + return TimeSpan.FromSeconds(_source.expires_in); + } + } + + bool IToken.hasExpired + { + get + { + TimeSpan age = DateTime.Now - _lastTokenRenewal; + return age > ExpiresIn; + } + } + } + + public class OAuth2ClientBuilder + { + private readonly string _clientId; + private readonly string _clientSecret; + private readonly Uri _tokenEndpoint; + private string _scope; + private IDictionary _additionalRequestParameters; + private HttpClientHandler _httpClientHandler; + + public OAuth2ClientBuilder(string clientId, string clientSecret, Uri tokenEndpoint) + { + _clientId = clientId ?? throw new ArgumentNullException(nameof(clientId)); + _clientSecret = clientSecret ?? throw new ArgumentNullException(nameof(clientSecret)); + _tokenEndpoint = tokenEndpoint ?? throw new ArgumentNullException(nameof(tokenEndpoint)); + + } + + public OAuth2ClientBuilder SetScope(string scope) + { + _scope = scope ?? throw new ArgumentNullException(nameof(scope)); + return this; + } + + public OAuth2ClientBuilder SetHttpClientHandler(HttpClientHandler handler) + { + _httpClientHandler = handler ?? throw new ArgumentNullException(nameof(handler)); + return this; + } + + public OAuth2ClientBuilder AddRequestParameter(string param, string paramValue) + { + if (param == null) + { + throw new ArgumentNullException("param is null"); + } + if (paramValue == null) + { + throw new ArgumentNullException("paramValue is null"); + } + if (_additionalRequestParameters == null) + { + _additionalRequestParameters = new Dictionary(); + } + _additionalRequestParameters[param] = paramValue; + return this; + } + + public IOAuth2Client Build() + { + return new OAuth2Client(_clientId, _clientSecret, _tokenEndpoint, + _scope, _additionalRequestParameters, _httpClientHandler); + } + } + + /** + * Default implementation of IOAuth2Client. It uses Client_Credentials OAuth2 flow to request a + * token. The basic constructor assumes no scopes are needed only the OAuth2 Client credentiuals. + * The additional constructor accepts a Dictionary with all the request parameters passed onto the + * OAuth2 request token. + */ + internal class OAuth2Client : IOAuth2Client, IDisposable + { + const string GRANT_TYPE = "grant_type"; + const string CLIENT_ID = "client_id"; + const string SCOPE = "scope"; + const string CLIENT_SECRET = "client_secret"; + const string REFRESH_TOKEN = "refresh_token"; + const string GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; + + private readonly string _clientId; + private readonly string _clientSecret; + private readonly Uri _tokenEndpoint; + private readonly string _scope; + private readonly IDictionary _additionalRequestParameters; + + public static readonly IDictionary EMPTY = new Dictionary(); + + private HttpClient _httpClient; + + public OAuth2Client(string clientId, string clientSecret, Uri tokenEndpoint, string scope, + IDictionary additionalRequestParameters, + HttpClientHandler httpClientHandler) + { + this._clientId = clientId; + this._clientSecret = clientSecret; + this._scope = scope; + this._additionalRequestParameters = additionalRequestParameters == null ? EMPTY : additionalRequestParameters; + this._tokenEndpoint = tokenEndpoint; + + _httpClient = httpClientHandler == null ? new HttpClient() : + new HttpClient(httpClientHandler); + _httpClient.DefaultRequestHeaders.Accept.Clear(); + _httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + } + + public IToken RequestToken() + { + var req = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint); + req.Content = new FormUrlEncodedContent(buildRequestParameters()); + + Task response = _httpClient.SendAsync(req); + response.Wait(); + response.Result.EnsureSuccessStatusCode(); + Task token = response.Result.Content.ReadFromJsonAsync(); + token.Wait(); + return new Token(token.Result); + } + + public IToken RefreshToken(IToken token) + { + if (token.RefreshToken == null) + { + throw new InvalidOperationException("Token has no Refresh Token"); + } + + var req = new HttpRequestMessage(HttpMethod.Post, _tokenEndpoint) + { + Content = new FormUrlEncodedContent(buildRefreshParameters(token)) + }; + + Task response = _httpClient.SendAsync(req); + response.Wait(); + response.Result.EnsureSuccessStatusCode(); + Task refreshedToken = response.Result.Content.ReadFromJsonAsync(); + refreshedToken.Wait(); + return new Token(refreshedToken.Result); + } + + public void Dispose() + { + _httpClient.Dispose(); + } + + private Dictionary buildRequestParameters() + { + var dict = new Dictionary(_additionalRequestParameters); + dict.Add(CLIENT_ID, _clientId); + dict.Add(CLIENT_SECRET, _clientSecret); + if (_scope != null && _scope.Length > 0) + { + dict.Add(SCOPE, _scope); + } + dict.Add(GRANT_TYPE, GRANT_TYPE_CLIENT_CREDENTIALS); + return dict; + } + + private Dictionary buildRefreshParameters(IToken token) + { + var dict = buildRequestParameters(); + dict.Remove(GRANT_TYPE); + dict.Add(GRANT_TYPE, REFRESH_TOKEN); + if (_scope != null) + { + dict.Add(SCOPE, _scope); + } + dict.Add(REFRESH_TOKEN, token.RefreshToken); + return dict; + } + } + + public class JsonToken + { + public JsonToken() + { + } + + public JsonToken(string access_token, string refresh_token, TimeSpan expires_in_span) + { + this.access_token = access_token; + this.refresh_token = refresh_token; + this.expires_in = (long)expires_in_span.TotalSeconds; + } + + public JsonToken(string access_token, string refresh_token, long expires_in) + { + this.access_token = access_token; + this.refresh_token = refresh_token; + this.expires_in = expires_in; + } + + public string access_token + { + get; set; + } + + public string refresh_token + { + get; set; + } + + public long expires_in + { + get; set; + } + } +} diff --git a/projects/RabbitMQ.Client.OAuth2/OAuth2CredentialsProvider.cs b/projects/RabbitMQ.Client.OAuth2/OAuth2CredentialsProvider.cs new file mode 100644 index 0000000000..08972f4e2f --- /dev/null +++ b/projects/RabbitMQ.Client.OAuth2/OAuth2CredentialsProvider.cs @@ -0,0 +1,142 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; +using System.Threading; + +namespace RabbitMQ.Client.OAuth2 +{ + public class OAuth2ClientCredentialsProvider : ICredentialsProvider + { + const int TOKEN_RETRIEVAL_TIMEOUT = 5000; + private ReaderWriterLock _lock = new ReaderWriterLock(); + + private readonly string _name; + private readonly IOAuth2Client _oAuth2Client; + private IToken _token; + + public OAuth2ClientCredentialsProvider(string name, IOAuth2Client oAuth2Client) + { + _name = name ?? throw new ArgumentNullException(nameof(name)); + _oAuth2Client = oAuth2Client ?? throw new ArgumentNullException(nameof(oAuth2Client)); + } + + public string Name + { + get + { + return _name; + } + } + + public string UserName + { + get + { + checkState(); + return string.Empty; + } + } + + public string Password + { + get + { + return checkState().AccessToken; + } + } + + public Nullable ValidUntil + { + get + { + IToken t = checkState(); + if (t is null) + { + return null; + } + else + { + return t.ExpiresIn; + } + } + } + + public void Refresh() + { + retrieveToken(); + } + + private IToken checkState() + { + _lock.AcquireReaderLock(TOKEN_RETRIEVAL_TIMEOUT); + try + { + if (_token != null) + { + return _token; + } + } + finally + { + _lock.ReleaseReaderLock(); + } + + return retrieveToken(); + } + + private IToken retrieveToken() + { + _lock.AcquireWriterLock(TOKEN_RETRIEVAL_TIMEOUT); + try + { + return requestOrRenewToken(); + } + finally + { + _lock.ReleaseReaderLock(); + } + } + + private IToken requestOrRenewToken() + { + if (_token == null || _token.RefreshToken == null) + { + _token = _oAuth2Client.RequestToken(); + } + else + { + _token = _oAuth2Client.RefreshToken(_token); + } + return _token; + } + } +} diff --git a/projects/RabbitMQ.Client.OAuth2/README.md b/projects/RabbitMQ.Client.OAuth2/README.md new file mode 100644 index 0000000000..dbcdcbe7bc --- /dev/null +++ b/projects/RabbitMQ.Client.OAuth2/README.md @@ -0,0 +1 @@ +# RabbitMQ .NET Client - OAuth2 diff --git a/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj b/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj new file mode 100644 index 0000000000..9e2c29ab48 --- /dev/null +++ b/projects/RabbitMQ.Client.OAuth2/RabbitMQ.Client.OAuth2.csproj @@ -0,0 +1,72 @@ + + + + net6.0;netstandard2.0 + $(NoWarn);CS1591 + true + RabbitMQ OAuth2 Client Library for .NET + VMware + VMware, Inc. or its affiliates. + Copyright © 2007-2023 VMware, Inc. or its affiliates. + The RabbitMQ OAuth2 Client Library for .NET enables OAuth2 token refresh for RabbitMQ.Client + true + true + icon.png + Apache-2.0 OR MPL-2.0 + https://www.rabbitmq.com/dotnet.html + rabbitmq, amqp, oauth2 + RabbitMQ + true + https://github.com/rabbitmq/rabbitmq-dotnet-client.git + true + snupkg + ../rabbit.snk + true + oauth2- + minimal + true + ../../packages + true + latest + 7.0 + README.md + + + + true + true + + + + + $(MinVerMajor).$(MinVerMinor).$(MinVerPatch)-$(MinVerPreRelease)-pr.$(CONCOURSE_PULL_REQUEST_NUMBER) + $(PackageVersion)+$(MinVerBuildMetadata) + $(PackageVersion) + + + + + + + + + + <_Parameter1>Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5 + + + <_Parameter1>Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5 + + + + + + + + + + + + + + + diff --git a/projects/RabbitMQ.Client.OAuth2/icon.png b/projects/RabbitMQ.Client.OAuth2/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..092bfef15cf639ddcce7bcde13e5d4b83787566e GIT binary patch literal 6766 zcmV-!8j zaB^>EX>4U6ba`-PAZ2)IW&i+q+O3&scH=s-g#Tj|y#&k&EC=H`y@R*>{t~1lk5%@) z&nlY|Nnyx{jErQN^?(1}=0E&In~I4^O0GFueqxQ)H{KL`{nYc<*?9h*pT{LWzdLW9 z7d)o|$8dd=cHiGPpFR)d`up>y+*f?L2f8mj1`Im0=goY(NY4A~yo>ihbzN_AE%J7$ z-CxIX-*}dC|ITcOo=S3E($D7Yi6}&ACMF8rui%0`f8(`+aQ!xZ1Al!e+-E~M@1r>v z^1TuQ@@{*NqqT1X^e)JccjjaCpYJ{d-?#gG_z=r{$A~vyUU2E_;GYn`nK(RG#>6+Vao#SwWB}TuzaP)roaL$z=zWM<$pr|jmy@VPjsQEDbGC&!Ff3C%ydFz>P zxWdfSajSO>=J?9DFY~91zj>Lxm88zu@{ASh^2%$Lp~&f9?g9|EZ`{%e@Z)vEZ{P4o zsfs3?Fc&u1Y`=!+$-TH04$gu562~iroRN7CK!}(-u^1OXz-AYc%g&1T;v8`-0MuAH z`QSSUxRm^4kv@iyy%A5!FgM>3ckcDcXRoKxCV)sG2LYQJ8eqkk;E%)thgu3Lrj&9L zge}$7a>y~KoU>#Vxh6rPB*`LDq)IEH#F9!bQc9_%RbK-dm};(4ORcpvZyFOd#%kQ( zIMaO(J@(XdmtK19Z9tz9M;dv^D5H)x{R|VCn0d-9v(C1hSt!MdE3Ld_l~q^UaBYVj zciMT&F1zmbMD0fP)9V+gxf?Zqkkb3)i5h2($YThna*`A?5cAQ2xF`Y;+AC(ZxEQ@6 zr;$gCua>65WzS<{CqsBjK*^%gdq)0LH+9dX}?P3{1? zOCK7Z)w1`o_US_RVJ$JfF`cj3QFu$6i9K3^(g^29+OPM8N)3nSE|ul`wlws_F!dmF zrD5GPWd$Otl#tfQlrmsITBzEZJ7bx;hd0n*<=j^lAKK;{IYlV5s`9EWIjBXST&rXZ ztnuRx6XXf)!cncupO1`D4V#7SurI*(T_@~Ik3f&JEtohJL7QcdGD)oSDd=B9*ZR!F z!PHH$+}nqgDmPlayra!+r_y%oqnt0y3B6YV?F}#V%t~xXpwYE1Bi&;npNp({7bVXi zs{^|2!|%?^_Ku}-2#Z9}^k}k!AM;?bW0TSC9m;bwk2hZ&@fBXs zi{$I-e}lrMN?HTrpbnGkWJnFn%95N-px%$GvyntZN-{!?hi+-48%Jye97eEtw$nvm| zayFL2q8~;NC#zA%y8ZY{m&|uUrAYyPnjx~N-H73R`HlSAt%*Te1cJ?S!V!0Jf?%`q zlN0QQRkTZ@_;lrjJaS@c+PxBjt!>Q+lzq%cF&cHf&|j&(4>m(QYD#Zt_@KGi%Qxez zG9@TC{1j4iTWa=6fy$&xE<5_Al9?0!#=b@h`@Kg?%*_e<74csr#OHi4|12Rs=Yx5l z5EI0>4CRYv?~CyS8eXvq-J}{iMD=6~`#SkzI^;+z60ij`WyeVTRO8l$gzmj6Is0Cf z4hWa_7P*Y-%@Ww?qG=_@1tuJ=I}WzT9C}i z9#$C!W0-TAQ5?$%b4rSMMAVGn#n7f2nyAUQaBPY}pjL5CHWPSzIKMGLW|8Dc#fm6$ zofLuNwQ8J6N~iDj072R*ge=9iqd-^jdQ4+HkdudH;{QtaB-9R4+3Orw@V*g<(!D zC;+!h96fTaJ|+S(S`IkGiibBjHMNXSPSi7JArpOSL2t@y`LHXQ!+O=LD8Kry=mci8 zy4BTU8C|cg<1x>*UI6#`uD(O!*;>6OyB9(+z|+SL-kYQ=6vzjg8J^9R%(PV=WY$$@ zEnrvCIa6HAX3^mdmgbAlO7+OdPxeIS>nxRmWlnsi+-ccUCP(sI8<7OY73q~0?yD%A zG*@;vdRe>G-bXe-XYKlmfu2?$chyBxPZLKwIwKNN>8NPJePg8T3XKlsjKapASP`1| z&g>$J)0t0Z_a7e3FTUU=JWu-9A*+vO2xZla=G>Kk+E9IeG(Wx35|2a!47+i3=~-;G z^6R3_tIXPea-`aY9w&sHG?XzqZR2a%UBRpLy0g)421?{YOwbhsnfIjnUFX^O}xiHWk;)R z&_X3Cb%gs$#lEp|X;H3BbC+beM84Uusz<3^hL&p`3OCXr>mwpF&{TKQ!3io#CGJhd zEMiOvLq|+0YGg{DvtTG#HNL}&^h~r%LR*~J+D#%os$-YkhDkQ0A%*@uWmP{WFOr$U z?TE$$6ecpOOVNss0nSgP%mpb1mSU(6$WGC|$o~T92edceTc;^}e{SrUq?jUs~J0A*BL#WU$Z0}lX5-Bfm1B>no!1qEC zfwU_b6(RIRpYz#u$ae{d)}cqioLB&K&9fMsrke7UQ$;+OLG2d86pzsYWFadk+Nob) z&z>H`fNfEBigp3&WUn4rO|Bkur)(oFX4{Ol)aSzZe-K6zeOm}3o1mj0SP+9ngq7`; z9wZw605(+}yUgfOF>k8HTsnPJ+^MT|H57@aniTh%0s2fiyV?d-H&CR>T~w9~N%+XI zsy-LbUE*Y*c|X6jYT$oZ4nJ2z^Y6;yUkCbIIsD93WB*Ng{PRE-^>!}}*B+Rn1I8t1 z>6EWr*Cg^(m=)+ET%k2U*y>PlhFUDDr-H3L6b*s$r;syr6<1t`{=1&Q-yiO1$YVwF zDRggc`}mFf3pqdIb&jTmr{t~s+(@YqVA95!p{g9T9$N3B8;Dui00RR@zxzo1D&lH%eh zxE37zSgbm@IP2=*DhPrfAWn`>iY`*(|B^zB7!Qv7@$TN^?j7K7RG4aZjRUG?8R=v~ z$mUjs;41=%B8&lyO3c*fL@@==@pTU$U+>~P%lq7)qfg134DboWvrIQE;tk^IO-tvz zPaI(-Ng+Nb9y92I#E)E;U4G+SblA@`BSt1YPaGi@iXAL>Fe@1<@f2}PQ8miYT&7<|$tLvo}5O@E;Pyr0oG<$(TMpm)vft+kKS2OvXTrEY+OLtvyx z+3Ozf?&<99-!rZLegJiZa;T-EdY1qI00v@9M??Vs0RI60puMM)00009a7bBm000ib z000ib0l1NC?EnA(2XskIMF->q91j=|1(u^W000Z2NklQ*YiN@fAXaWXf;sY2>6k|wy;6WnBCOl|Bjf99u z#1Ei=5d1AicOz4w2w z^yb-^-#EleC5MIajkAUarknu9>|MlSi}G+(@bL)ide zc=^#c`1x1kY{v_z3dm9*mH%W4fuuRa&L3{##fSDg8^z(*_wv(QPGj=W222j%?sE)0 z4x?BrSTzX3a}N&l;MWg=9w-+8p4k!8o*BxvuI9^TW$8_K+fzY1HJ-XVb~cI^9 z8vzBiW=RX*6PwM2D5NS#D*WkZLrfkSDi;8DJ+%q5)a`N#`gHH}qt2TD_tRsjbvR8{ zP!%TrJIbM5X}JK<8n0z)e>ryj2>{TUHO`t(nhkK*Jc6Q<&C$$T6XgPcKxXIE%9gSL zKs;<3b=r;7Akab*NZCkW3AAPud7LqAmrMirvT069pz$;WMbdZ5#{tAK6|~u#`8ChW zN)`poy#P-PP#t#EZdq?jW24i`rvbdEPlqdHRlRvKDpC0WposHOt>BKTo5&-O%N7YN zLnydb6K_L^I9fgpARgj@Wg$>D4X=Y&od#&PQa|ugAPTR8SDmPO3};JRJ`LcU1ZF8g z4oRRtwQS5NvA72LR_@fPyvd+HbW}e<^1g zNZ^1bz+FeyYeGoLB7q`)eDug+BrrDWWekF1qO!dXE)pDzFr3_0#dTq!Kx*E z$+>3P?22fB>@c-+7&ufgvn_w;4s5cyYyj}Yom?)E^%>5L0{~Xl;?s=;dQ#1%Q%*0y zbtqUI#sM02Kd~S|G3M={0S3gt+UI{|%?WfK2^3K4E)LLYTW>RFZO92+bt=GGj~d2C z0RXEF0a4`f@Ojw|hWH?Xs^T<+9@ji_nOb-qTv-yBGr<6W!L)$egCX9=Atihr?Ai-Z zaT`KURs7`xq_v;{1~LH-0pRUih=AIXMgpDAn?e-3Q`18YPoZE1qa}?5I#;0%4^(L4 zkz!iP2>`e1UrAc-`kJ0dz0q^kP30qj&HLSxJdLOO2E+L8&SQD8L`-oV7e+OcSsZzW) z#paP0BUls>K`m&v4U@%91npIE7tK@aRY3%sABPjG*^dZnvj5BSJf%mAR3MFn4eL|R z{lFBLe__JD@4C7%oRa|{Nh6FW0N4m(YqQ``9vxB^cyWt7#=N72`K=%k>Kyt8ra^!S?rAhogxhzfn1`Je56ETslh@X#F_>!+RFjJ;2x7V3rdG*02cP z)h5N~U+mb!@s2|D^VyS*`*?kvBwmqB)p_#XF+(^MP)&(Y_WxzXUx}C?t4l0GYbuVz zOjK1=avG*#E-JeIDB`b9V31W91wwt+2@JBzTo^=cAc0jzfQXtf4^=h*m_Pz6y#9d% z1|vWqfz@99zyYd`g5U*IoCpLGSlOlTl0YfEiw{yE<*#`pFy{lo3#cRrG}H;KxOl7+ z7-ZFP0K^10Flc}v%dnjkW9%Y{eaid{&xLs+&dj?f*DxCWM;P_Y7T~ZS-QRX z)~>akn`Kc%6kUN!C=@_#NC{h(sWc#Bd-=|bW^_xz_UeM~OEHh!D-nqEXA8T(A9b(! zsT+w?>ca^aUD@QqD@NG1J)x5vrPFQ#uzu_;Kk-)w;jUtf+3-7&boX`H?AZK0>S!9zz8iO=xmH!wz zW4!uAhkL)=;_#jkP|1&iiBkmVO>s(^O4;h*JCi6%un3oa?j5ebYl`6w?_Ne*GA;*R znq|kQhd8i%)NOGk0TZdGV$tQY_-s9Yicfv-;Bqd0z9bttqAaQd>X6C7;+58N1zKaApq|U-Nc$0@!U0 zZ%@_2OC&=h!r0lP2D8y!C+2*H$2t(F&f-N<%n1zA$J%%*7T(wpgK=&KLOwwMTPO9> z&B%srh)a%^g#tr9K>v2r-#l5DwG}AU> zfWCL8pGZ)<)51jRa`)#XFsx~RzPYFS0z?~N9Cp?_7!nZBmn5kt5*TgEY7ILDStt?H zw{)iccuvj3tt@9#+^GPS@G_`h7l6)mgBR{QM55#FRwjLor+?86*E<|chy?a;HxFaMpiI_p`rn2v^>v;G(6VCehK!MU%Yo;ua!Rth#+2ELhQ-Ybi+*vV!|Eoq4vl zM3!}9Z7#jO#X0YPi>>FisE=&LL>mERrnwhTqIf++jjbTinmI(8G%*;4)}Kqe3Hx7| z=J`iD?0IY>X%c0$+=|=VTv2Lq8UV1QNi>D&jKE-#B;yHlc+oulY7RWu+_sD6SHlQa zC9m=CXB`xXV;c^JEJ+#IA~zt9uVy!iKvt4n^Q&5py#++F%d`+M7Q=kt_|RlZel#o` z4qYC*U~G1|)xx8H$KQn+2(ZzLP;l*@4i2&$vneZjhtVLbAMybLYC|Zv;-)J^0;|nh zLY=_sDt|(qzyN}f6Igi(bpk8P0R%ME39LL33;?J!3+%oQ4*38TU4IQFVyaFALL{)l z_MZ?53^k8WxExVgdxJBoQgKk&fX# zmrh_)g9LY=Qj!vJ&IhMsuD)f6*Pdyiof<|hYI4&M=BPy4J}(NVf-x40P_NH&)y?Zf zRpF64#`*Pk8`yRoQ6-WTn=W4=ghg-AIN@id_uH}m>ISY(z0Ku*PF>(k&zt0V-`3ty zfKzEx`h3xZz78dkBe-Tdf zOj(;f_bWeIlwI9+?3fna#V2UfTAi2KwQ!tWX$+-YaK#KCyGgk4+EIZ21Au|U{O}FX Q{Qv*}07*qoM6N<$f|tF>D*ylh literal 0 HcmV?d00001 diff --git a/projects/RabbitMQ.Client/README.md b/projects/RabbitMQ.Client/README.md new file mode 100644 index 0000000000..5cf0d0cd26 --- /dev/null +++ b/projects/RabbitMQ.Client/README.md @@ -0,0 +1 @@ +# RabbitMQ .NET Client diff --git a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj index bce32216b3..1a51baf82a 100644 --- a/projects/RabbitMQ.Client/RabbitMQ.Client.csproj +++ b/projects/RabbitMQ.Client/RabbitMQ.Client.csproj @@ -25,10 +25,11 @@ v minimal true - ..\..\packages + ../../packages true latest 7.0 + README.md @@ -48,6 +49,7 @@ + <_Parameter1>Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5 diff --git a/projects/RabbitMQ.Client/client/api/BasicCredentialsProvider.cs b/projects/RabbitMQ.Client/client/api/BasicCredentialsProvider.cs new file mode 100644 index 0000000000..5357897529 --- /dev/null +++ b/projects/RabbitMQ.Client/client/api/BasicCredentialsProvider.cs @@ -0,0 +1,76 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; + +namespace RabbitMQ.Client +{ + public class BasicCredentialsProvider : ICredentialsProvider + { + private readonly string _name; + private readonly string _userName; + private readonly string _password; + + public BasicCredentialsProvider(string name, string userName, string password) + { + _name = name ?? string.Empty; + _userName = userName ?? throw new ArgumentNullException(nameof(userName)); + _password = password ?? throw new ArgumentNullException(nameof(password)); + } + + public string Name + { + get { return _name; } + } + + public string UserName + { + get { return _userName; } + } + + public string Password + { + get { return _password; } + } + + public Nullable ValidUntil + { + get + { + return null; + } + } + + public void Refresh() + { + } + } +} diff --git a/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs b/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs index 9c66e26cd4..4ff107df22 100644 --- a/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs +++ b/projects/RabbitMQ.Client/client/api/ConnectionConfig.cs @@ -55,7 +55,14 @@ public sealed class ConnectionConfig /// /// Password to use when authenticating to the server. /// - public string Password { get; internal set; } + public string Password { get; } + + /// + /// Default CredentialsProvider implementation. If set, this + /// overrides UserName / Password + /// + public ICredentialsProvider CredentialsProvider; + public ICredentialsRefresher CredentialsRefresher; /// /// SASL auth mechanisms to use. @@ -76,6 +83,7 @@ public sealed class ConnectionConfig /// Maximum channel number to ask for. /// public ushort MaxChannelCount { get; } + /// /// Frame-max parameter to ask for (in bytes). /// @@ -136,7 +144,9 @@ public sealed class ConnectionConfig internal Func FrameHandlerFactory { get; } - internal ConnectionConfig(string virtualHost, string userName, string password, IList authMechanisms, + internal ConnectionConfig(string virtualHost, string userName, string password, + ICredentialsProvider credentialsProvider, ICredentialsRefresher credentialsRefresher, + IList authMechanisms, IDictionary clientProperties, string? clientProvidedName, ushort maxChannelCount, uint maxFrameSize, bool topologyRecoveryEnabled, TopologyRecoveryFilter topologyRecoveryFilter, TopologyRecoveryExceptionHandler topologyRecoveryExceptionHandler, @@ -147,6 +157,8 @@ internal ConnectionConfig(string virtualHost, string userName, string password, VirtualHost = virtualHost; UserName = userName; Password = password; + CredentialsProvider = credentialsProvider ?? new BasicCredentialsProvider(clientProvidedName, userName, password); + CredentialsRefresher = credentialsRefresher; AuthMechanisms = authMechanisms; ClientProperties = clientProperties; ClientProvidedName = clientProvidedName; diff --git a/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs b/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs index e4cd1171f1..bbe2a10579 100644 --- a/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs +++ b/projects/RabbitMQ.Client/client/api/ConnectionFactory.cs @@ -160,6 +160,8 @@ public sealed class ConnectionFactory : ConnectionFactoryBase, IConnectionFactor /// public static System.Net.Sockets.AddressFamily DefaultAddressFamily { get; set; } + public static readonly ICredentialsRefresher DefaultCredentialsRefresher = new NoOpCredentialsRefresher(); + /// /// Set to false to disable automatic connection recovery. /// Defaults to true. @@ -307,11 +309,26 @@ public AmqpTcpEndpoint Endpoint ["information"] = Encoding.UTF8.GetBytes("Licensed under the MPL. See https://www.rabbitmq.com/") }; + /// + /// Username to use when authenticating to the server. + /// + public string UserName { get; set; } = DefaultUser; + /// /// Password to use when authenticating to the server. /// public string Password { get; set; } = DefaultPass; + /// + /// CredemtialsProvider used to obtain username and pasword. + /// + public ICredentialsProvider CredentialsProvider { get; set; } + + /// + /// Used to refresh credentials. + /// + public ICredentialsRefresher CredentialsRefresher { get; set; } = DefaultCredentialsRefresher; + /// /// Maximum channel number to ask for. /// @@ -327,11 +344,6 @@ public AmqpTcpEndpoint Endpoint /// public TimeSpan RequestedHeartbeat { get; set; } = DefaultHeartbeat; - /// - /// Username to use when authenticating to the server. - /// - public string UserName { get; set; } = DefaultUser; - /// /// Virtual host to access during this connection. /// @@ -540,6 +552,8 @@ private ConnectionConfig CreateConfig(string clientProvidedName) VirtualHost, UserName, Password, + CredentialsProvider, + CredentialsRefresher, AuthMechanisms, ClientProperties, clientProvidedName, diff --git a/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs b/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs index 4f8736baa6..b1480cf887 100644 --- a/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs +++ b/projects/RabbitMQ.Client/client/api/IConnectionFactory.cs @@ -73,6 +73,14 @@ public interface IConnectionFactory /// string VirtualHost { get; set; } + /// + /// Credentials provider. It is optional. When set, username and password + /// are obtained thru this provider. + /// + ICredentialsProvider CredentialsProvider { get; set; } + + ICredentialsRefresher CredentialsRefresher { get; set; } + /// /// Sets or gets the AMQP Uri to be used for connections. /// diff --git a/projects/RabbitMQ.Client/client/api/ICredentialsProvider.cs b/projects/RabbitMQ.Client/client/api/ICredentialsProvider.cs new file mode 100644 index 0000000000..2bb365f80d --- /dev/null +++ b/projects/RabbitMQ.Client/client/api/ICredentialsProvider.cs @@ -0,0 +1,53 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +namespace RabbitMQ.Client +{ + public interface ICredentialsProvider + { + string Name { get; } + string UserName { get; } + string Password { get; } + + /// + /// If credentials have an expiry time this property returns the interval. + /// Otherwise, it returns null. + /// + System.TimeSpan? ValidUntil { get; } + + /// + /// Before the credentials are available, be it Username, Password or ValidUntil, + /// the credentials must be obtained by calling this method. + /// And to keep it up to date, this method must be called before the ValidUntil interval. + /// + void Refresh(); + } +} diff --git a/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs new file mode 100644 index 0000000000..3cd10caf0f --- /dev/null +++ b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs @@ -0,0 +1,140 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Diagnostics.Tracing; +namespace RabbitMQ.Client +{ + public interface ICredentialsRefresher + { + ICredentialsProvider Register(ICredentialsProvider provider, NotifyCredentialRefreshed callback); + bool Unregister(ICredentialsProvider provider); + + delegate void NotifyCredentialRefreshed(bool succesfully); + } + + [EventSource(Name = "TimerBasedCredentialRefresher")] + public class TimerBasedCredentialRefresherEventSource : EventSource + { + public static TimerBasedCredentialRefresherEventSource Log { get; } = new TimerBasedCredentialRefresherEventSource(); + + [Event(1)] + public void Registered(string name) => WriteEvent(1, "Registered", name); + [Event(2)] + public void Unregistered(string name) => WriteEvent(2, "nNregistered", name); + [Event(3)] + public void ScheduledTimer(string name, double interval) => WriteEvent(3, "ScheduledTimer", name, interval); + [Event(4)] + public void TriggeredTimer(string name) => WriteEvent(4, "TriggeredTimer", name); + [Event(5)] + public void RefreshedCredentials(string name, bool succesfully) => WriteEvent(5, "RefreshedCredentials", name, succesfully); + } + + public class TimerBasedCredentialRefresher : ICredentialsRefresher + { + private Dictionary _registrations = new Dictionary(); + + public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback) + { + if (!provider.ValidUntil.HasValue || provider.ValidUntil.Value.Equals(TimeSpan.Zero)) + { + return provider; + } + + _registrations.Add(provider, scheduleTimer(provider, callback)); + TimerBasedCredentialRefresherEventSource.Log.Registered(provider.Name); + return provider; + } + + public bool Unregister(ICredentialsProvider provider) + { + if (!_registrations.ContainsKey(provider)) + { + return false; + } + + var timer = _registrations[provider]; + if (timer != null) + { + TimerBasedCredentialRefresherEventSource.Log.Unregistered(provider.Name); + timer.Stop(); + _registrations.Remove(provider); + timer.Dispose(); + return true; + } + else + { + return false; + } + } + + private System.Timers.Timer scheduleTimer(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback) + { + System.Timers.Timer timer = new System.Timers.Timer(); + timer.Interval = provider.ValidUntil.Value.TotalMilliseconds * (1.0 - (1 / 3.0)); + timer.Elapsed += (o, e) => + { + TimerBasedCredentialRefresherEventSource.Log.TriggeredTimer(provider.Name); + try + { + provider.Refresh(); + scheduleTimer(provider, callback); + callback.Invoke(provider.Password != null); + TimerBasedCredentialRefresherEventSource.Log.RefreshedCredentials(provider.Name, true); + } + catch (Exception) + { + callback.Invoke(false); + TimerBasedCredentialRefresherEventSource.Log.RefreshedCredentials(provider.Name, false); + } + + }; + timer.Enabled = true; + timer.AutoReset = false; + TimerBasedCredentialRefresherEventSource.Log.ScheduledTimer(provider.Name, timer.Interval); + return timer; + } + } + + class NoOpCredentialsRefresher : ICredentialsRefresher + { + public ICredentialsProvider Register(ICredentialsProvider provider, ICredentialsRefresher.NotifyCredentialRefreshed callback) + { + return provider; + } + + public bool Unregister(ICredentialsProvider provider) + { + return false; + } + } +} diff --git a/projects/RabbitMQ.Client/client/api/PlainMechanism.cs b/projects/RabbitMQ.Client/client/api/PlainMechanism.cs index 69bb501b08..964e624c29 100644 --- a/projects/RabbitMQ.Client/client/api/PlainMechanism.cs +++ b/projects/RabbitMQ.Client/client/api/PlainMechanism.cs @@ -37,7 +37,7 @@ public class PlainMechanism : IAuthMechanism { public byte[] handleChallenge(byte[] challenge, ConnectionConfig config) { - return Encoding.UTF8.GetBytes($"\0{config.UserName}\0{config.Password}"); + return Encoding.UTF8.GetBytes($"\0{config.CredentialsProvider.UserName}\0{config.CredentialsProvider.Password}"); } } } diff --git a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs index f767e12971..625ba1b2d4 100644 --- a/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs +++ b/projects/RabbitMQ.Client/client/impl/AutorecoveringConnection.cs @@ -175,15 +175,12 @@ public override string ToString() internal IFrameHandler FrameHandler => InnerConnection.FrameHandler; - internal string Password => _config.Password; - ///API-side invocation of updating the secret. public void UpdateSecret(string newSecret, string reason) { ThrowIfDisposed(); EnsureIsOpen(); _innerConnection.UpdateSecret(newSecret, reason); - _config.Password = newSecret; } ///API-side invocation of connection.close with timeout. diff --git a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs index af3d414370..ea561d6986 100644 --- a/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs +++ b/projects/RabbitMQ.Client/client/impl/Connection.Commands.cs @@ -160,10 +160,28 @@ private async ValueTask StartAndTuneAsync() _channel0.ConnectionTuneOk(channelMax, frameMax, (ushort)Heartbeat.TotalSeconds); + MaybeStartCredentialRefresher(); + // now we can start heartbeat timers MaybeStartHeartbeatTimers(); } + private void MaybeStartCredentialRefresher() + { + if (_config.CredentialsProvider.ValidUntil != null) + { + _config.CredentialsRefresher.Register(_config.CredentialsProvider, NotifyCredentialRefreshed); + } + } + + private void NotifyCredentialRefreshed(bool succesfully) + { + if (succesfully) + { + UpdateSecret(_config.CredentialsProvider.Password, "Token refresh"); + } + } + private IAuthMechanismFactory GetAuthMechanismFactory(string supportedMechanismNames) { // Our list is in order of preference, the server one is not. diff --git a/projects/TestApplications/CreateChannel/CreateChannel.csproj b/projects/TestApplications/CreateChannel/CreateChannel.csproj index 01275f734c..9fe7c226d7 100644 --- a/projects/TestApplications/CreateChannel/CreateChannel.csproj +++ b/projects/TestApplications/CreateChannel/CreateChannel.csproj @@ -13,7 +13,7 @@ - + diff --git a/projects/TestApplications/MassPublish/MassPublish.csproj b/projects/TestApplications/MassPublish/MassPublish.csproj index 01275f734c..9fe7c226d7 100644 --- a/projects/TestApplications/MassPublish/MassPublish.csproj +++ b/projects/TestApplications/MassPublish/MassPublish.csproj @@ -13,7 +13,7 @@ - + diff --git a/projects/TestApplications/OAuth2/OAuth2.csproj b/projects/TestApplications/OAuth2/OAuth2.csproj new file mode 100644 index 0000000000..65fd6bff4c --- /dev/null +++ b/projects/TestApplications/OAuth2/OAuth2.csproj @@ -0,0 +1,22 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + + + + + + + diff --git a/projects/TestApplications/OAuth2/Program.cs b/projects/TestApplications/OAuth2/Program.cs new file mode 100644 index 0000000000..4b71854664 --- /dev/null +++ b/projects/TestApplications/OAuth2/Program.cs @@ -0,0 +1,153 @@ +using System.Text; +using Microsoft.Extensions.Configuration; +using RabbitMQ.Client; +using RabbitMQ.Client.Events; +using RabbitMQ.Client.OAuth2; + +namespace OAuth2 +{ + public static class Program + { + private static string _exchange = "test_direct"; + private static AutoResetEvent? _doneEvent; + + public class OAuth2Options + { + public string? Name { get; set; } + public string? ClientId { get; set; } + public string? ClientSecret { get; set; } + public string? Scope { get; set; } + public string? TokenEndpoint { get; set; } + public int TokenExpiresInSeconds { get; set; } + } + + public static void Main(string[] args) + { + + IConfiguration configuration = new ConfigurationBuilder() + .AddEnvironmentVariables() + .AddCommandLine(args) + .Build(); + + _doneEvent = new AutoResetEvent(false); + + OAuth2Options? oauth2Options = configuration.Get(); + if (oauth2Options == null) + { + throw new ArgumentException("There are no OAuth2 Options"); + } + + var connectionFactory = new ConnectionFactory + { + AutomaticRecoveryEnabled = true, + CredentialsProvider = GetCredentialsProvider(oauth2Options), + CredentialsRefresher = GetCredentialsRefresher() + }; + + using (IConnection connection = connectionFactory.CreateConnection()) + { + using (IChannel publisher = declarePublisher(connection)) + using (IChannel subscriber = declareConsumer(connection)) + { + Publish(publisher); + Consume(subscriber); + + if (oauth2Options.TokenExpiresInSeconds > 0) + { + for (int i = 0; i < 4; i++) + { + Console.WriteLine("Wait until Token expires. Attempt #" + (i + 1)); + Thread.Sleep((oauth2Options.TokenExpiresInSeconds + 10) * 1000); + Console.WriteLine("Resuming .."); + Publish(publisher); + _doneEvent.Reset(); + Consume(subscriber); + } + } + } + } + } + + private static ICredentialsRefresher GetCredentialsRefresher() + { + return new TimerBasedCredentialRefresher(); + } + + public static IChannel declarePublisher(IConnection connection) + { + var publisher = connection.CreateChannel(); + publisher.ConfirmSelect(); + publisher.ExchangeDeclare("test_direct", ExchangeType.Direct, true, false); + return publisher; + } + + public static void Publish(IChannel publisher) + { + const string message = "Hello World!"; + + var body = new ReadOnlyMemory(Encoding.UTF8.GetBytes(message)); + var properties = new BasicProperties + { + AppId = "oauth2", + }; + + publisher.BasicPublish(exchange: _exchange, routingKey: "hello", basicProperties: properties, body: body); + + Console.WriteLine("Sent message"); + publisher.WaitForConfirmsOrDieAsync().Wait(); + Console.WriteLine("Confirmed Sent message"); + } + + public static IChannel declareConsumer(IConnection connection) + { + var subscriber = connection.CreateChannel(); + subscriber.QueueDeclare("testqueue", true, false, false); + subscriber.QueueBind("testqueue", _exchange, "hello"); + return subscriber; + } + + public static void Consume(IChannel subscriber) + { + var asyncListener = new AsyncEventingBasicConsumer(subscriber); + asyncListener.Received += AsyncListener_Received; + string consumerTag = subscriber.BasicConsume("testqueue", true, "testconsumer", asyncListener); + _doneEvent?.WaitOne(1); + Console.WriteLine("Received message"); + subscriber.BasicCancel(consumerTag); + } + + private static Task AsyncListener_Received(object sender, BasicDeliverEventArgs @event) + { + _doneEvent?.Set(); + return Task.CompletedTask; + } + + public static OAuth2ClientCredentialsProvider GetCredentialsProvider(OAuth2Options? options) + { + if (options == null) + { + throw new ArgumentException("There are no OAuth2 Options"); + } + + if (options.ClientId == null) + { + throw new ArgumentNullException("oauth2Options.ClientId is null"); + } + + if (options.TokenEndpoint == null) + { + throw new ArgumentException("oauth2Options.TokenEndpoint is null"); + } + + Console.WriteLine("OAuth2Client "); + Console.WriteLine("- ClientId: " + options.ClientId); + Console.WriteLine("- ClientSecret: " + options.ClientSecret); + Console.WriteLine("- TokenEndpoint: " + options.TokenEndpoint); + Console.WriteLine("- Scope: " + options.Scope); + + var tokenEndpointUri = new Uri(options.TokenEndpoint); + var oAuth2Client = new OAuth2ClientBuilder(options.ClientId, options.ClientSecret, tokenEndpointUri).Build(); + return new OAuth2ClientCredentialsProvider(options.Name, oAuth2Client); + } + } +} diff --git a/projects/TestApplications/OAuth2/README.md b/projects/TestApplications/OAuth2/README.md new file mode 100644 index 0000000000..3392315d03 --- /dev/null +++ b/projects/TestApplications/OAuth2/README.md @@ -0,0 +1,33 @@ +# OAuth2 Test Application + +Application to test and demonstrate OAuth2 authorization and token refresh +in a .Net Console application. + +There are Authorization servers like UAA which do not return a `refresh_token` +when using *Client credentials* flow to request a token. This means that the +.Net client must request a new token before it expires. Whereas there are other +Authorization servers like Keycloak which does return one. This means that the +.Net client refreshes the token before it expires. + +To test against UAA follow these steps: +1. Start UAA -> `./start-uaa.sh` +2. Start RabbitMQ with MODE=uaa -> `MODE=uaa ./start-rabbit.sh` +3. Start Test with MODE=uaa -> `MODE=uaa ./start-test.sh` + +To test against Keycloak follow these steps: +1. Start Keycloak -> `./start-keycloak.sh` +2. Start RabbitMQ with MODE=keycloak -> `MODE=keycloak ./start-rabbit.sh` +3. Start Test with MODE=keycloak -> `MODE=keycloak ./start-test.sh` + +## Authorization Servers + +Both Authorization servers have the following relevant configuration: +- An oauth client called `producer` +- A RSA signing key +- Access Token lifetime of 1 minute + +## Test Application + +The Test Application declares an exchange, a queue, binds the queue to the exchange, +and sends 4 messages and waits for them and in between each message it waits until +the token has expired to force a token refresh. diff --git a/projects/TestApplications/OAuth2/common.sh b/projects/TestApplications/OAuth2/common.sh new file mode 100755 index 0000000000..c6922ba05a --- /dev/null +++ b/projects/TestApplications/OAuth2/common.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +readonly docker_name_prefix='rabbitmq-dotnet-client-oauth2' + +function docker_stop +{ + local -r name="$1" + local -r docker_name="$docker_name_prefix-$name" + + set +o errexit + if docker stop "$docker_name" + then + docker rm "$docker_name" + fi + set -o errexit +} diff --git a/projects/TestApplications/OAuth2/enabled_plugins b/projects/TestApplications/OAuth2/enabled_plugins new file mode 100644 index 0000000000..0bd9a01a1e --- /dev/null +++ b/projects/TestApplications/OAuth2/enabled_plugins @@ -0,0 +1 @@ +[rabbitmq_management,rabbitmq_auth_backend_oauth2]. diff --git a/projects/TestApplications/OAuth2/keycloak/import/test-realm.json b/projects/TestApplications/OAuth2/keycloak/import/test-realm.json new file mode 100644 index 0000000000..df2b7594f8 --- /dev/null +++ b/projects/TestApplications/OAuth2/keycloak/import/test-realm.json @@ -0,0 +1,2488 @@ +{ + "id" : "test", + "realm" : "test", + "notBefore" : 0, + "defaultSignatureAlgorithm" : "RS256", + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 60, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "clientSessionIdleTimeout" : 0, + "clientSessionMaxLifespan" : 0, + "clientOfflineSessionIdleTimeout" : 0, + "clientOfflineSessionMaxLifespan" : 0, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "oauth2DeviceCodeLifespan" : 600, + "oauth2DevicePollingInterval" : 5, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : false, + "loginWithEmailAllowed" : true, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "2b61bc53-60cc-48fc-b89b-ee3e80204895", + "name" : "rabbitmq.tag:management", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "c28bf7ca-9fb7-485c-a68b-d5fb4bd844fb", + "name" : "rabbitmq.tag:administrator", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "d2b776e4-8c4d-4168-9d52-76aaa115ee70", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "6faef857-1c9b-4474-ba01-ad1946d243d6", + "name" : "rabbitmq-proxy-client-role", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "0a838a26-4908-4750-a1d0-7cc322c698ae", + "name" : "producer", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "dd893988-6661-4849-a0f1-1cd1a63b51a5", + "name" : "rabbitmq.read:*/*", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "6feb7afe-2fa8-4569-8fb8-e50c2a4302d2", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "af1bc955-6d4d-42e9-b0d4-343e7eb075d0", + "name" : "rabbitmq-role", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "77e9131f-1eb3-45a3-9f3b-f74991a99def", + "name" : "rabbitmq.configure:*/*", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "97bb2b6b-33ff-404e-b754-351604d9f34c", + "name" : "rabbitmq", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "b84ae322-7112-41d1-8a3f-0009447ded47", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "view-profile", "manage-account" ] + } + }, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "5516969b-be85-490c-9715-9c1186075d60", + "name" : "rabbitmq-management", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + }, { + "id" : "216cfa85-9b8a-4fc0-bee1-814e2978d82b", + "name" : "rabbitmq.write:*/*", + "composite" : false, + "clientRole" : false, + "containerId" : "test", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "6721a146-c9e3-4a24-9d26-6dbc7e3aae1f", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "147b7e9a-d884-42b7-a970-245c2b5590b0", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "c25f4711-ee9b-4457-9636-7dacffceb676", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "1ce1e692-4dae-498f-8ac6-ca119eb329ef", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "78cd990d-68bd-4e71-9561-5e4412bcbfb7", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "c5bc4413-71cb-43f1-b48b-c9428aed47cd", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "21b62a0b-62fd-4a39-8b97-8ce8b89ad9d8", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "9df26a88-36e9-4670-8b63-dc4e57ebcce8", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "02bc109a-d318-4196-aa15-171651685b50", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-users", "query-groups" ] + } + }, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "97499d4c-fb81-4ee6-bd5e-6eb198424654", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "bf64efcd-f8a4-47e6-bc5e-0ff29635b885", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "manage-authorization", "view-clients", "view-events", "query-realms", "create-client", "view-identity-providers", "query-clients", "view-users", "manage-clients", "manage-events", "view-realm", "manage-realm", "manage-users", "query-users", "query-groups", "manage-identity-providers", "view-authorization", "impersonation" ] + } + }, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "fb251ef8-0f7e-4e85-a423-e3bf515dbe5c", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "09df7745-f99e-4961-add6-eca3e2ab9b44", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "45a27cee-8828-4427-90b4-9394b080db18", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "7454f27b-cabc-4160-9835-1747659f6f00", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "42d9a084-b4e0-42f5-8c29-9623fb265f79", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "893a1a00-5e1f-4dc2-983d-640a3cce58fa", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "8c9b5f3e-2819-4dbe-81d0-fa8721ff9f1d", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + }, { + "id" : "1e3044c3-fb93-48fa-9b27-eb4d8c6ccad7", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "attributes" : { } + } ], + "mgt_api_client" : [ ], + "security-admin-console" : [ ], + "admin-cli" : [ ], + "producer" : [ ], + "account-console" : [ ], + "broker" : [ { + "id" : "147bafb6-45a8-45ba-b214-7826b1fc4856", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "f32cd0e1-5b78-412a-ba07-6ad2a9aeb007", + "attributes" : { } + } ], + "rabbitmq-client-code" : [ ], + "rabbitmq" : [ { + "id" : "f5caa7a5-0770-41d8-a3a3-8691470b6d82", + "name" : "rabbitmq-role", + "description" : "", + "composite" : false, + "clientRole" : true, + "containerId" : "a57c9f6a-8b64-47dc-af53-d6ccc2d4aa60", + "attributes" : { } + } ], + "account" : [ { + "id" : "957f712c-e735-402d-9f41-ad9832749f51", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + }, { + "id" : "9145b32b-f8ef-4ff0-b50a-be1af192a65a", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + }, { + "id" : "59cac6df-51cd-4a3c-bf77-03bc2b34fe69", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + }, { + "id" : "508ce853-78d5-428c-9589-0e310fa7fe40", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + }, { + "id" : "061542b2-67d9-4388-aadb-9c936f19d607", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + }, { + "id" : "6d326537-afdd-4f72-8973-14b164361a7e", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + }, { + "id" : "1fc2ea3e-395a-45bd-ae2f-a9eb674ed4b2", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + }, { + "id" : "95ad82e5-1859-496e-ba95-6b38f8043efd", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "attributes" : { } + } ], + "rabbitmq-proxy-client" : [ { + "id" : "ba66d339-cbca-41c1-87fe-38e7b50efd52", + "name" : "rabbitmq-proxy-client-role", + "composite" : true, + "composites" : { + "realm" : [ "rabbitmq-role", "rabbitmq-proxy-client-role", "rabbitmq" ] + }, + "clientRole" : true, + "containerId" : "c265f3db-ed3a-4898-8800-af044b3c30f5", + "attributes" : { } + } ] + } + }, + "groups" : [ { + "id" : "6746dbec-7e2b-4540-ae00-73aa2a93a04e", + "name" : "rabbitmq", + "path" : "/rabbitmq", + "attributes" : { }, + "realmRoles" : [ "rabbitmq" ], + "clientRoles" : { }, + "subGroups" : [ ] + } ], + "defaultRole" : { + "id" : "b84ae322-7112-41d1-8a3f-0009447ded47", + "name" : "default-roles-test", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "test" + }, + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpPolicyCodeReusable" : false, + "otpSupportedApplications" : [ "totpAppFreeOTPName", "totpAppGoogleName" ], + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "users" : [ { + "id" : "4cf4d6b5-09e5-453f-bf22-c8efdc2dd1dc", + "createdTimestamp" : 1651841525973, + "username" : "rabbit_admin", + "enabled" : true, + "totp" : false, + "emailVerified" : true, + "firstName" : "", + "lastName" : "", + "email" : "rabbit_admin@rabbit.com", + "credentials" : [ { + "id" : "deca2be2-28ad-4f98-981f-3ec68bf12ae2", + "type" : "password", + "createdDate" : 1651841816533, + "secretData" : "{\"value\":\"bRuz2IKP4+kG3IKo258mVNqW8Nts6CkZavF3tf4M+/dlJFNPJIallxephOKUiVPtMOdO9Huq9K0uwTBYSZY3fg==\",\"salt\":\"v2qUXLV0n8402Ef8brQg1Q==\",\"additionalParameters\":{}}", + "credentialData" : "{\"hashIterations\":27500,\"algorithm\":\"pbkdf2-sha256\",\"additionalParameters\":{}}" + } ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "rabbitmq.tag:administrator", "rabbitmq.configure:*/*", "rabbitmq", "rabbitmq.write:*/*", "rabbitmq.read:*/*" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "15f03347-e2fc-4f8c-9743-f4dfd59f67fe", + "createdTimestamp" : 1652084304711, + "username" : "service-account-mgt_api_client", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "serviceAccountClientId" : "mgt_api_client", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test", "rabbitmq-management" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "63ec2047-6689-45c0-981d-f9b127a6bb7f", + "createdTimestamp" : 1652084012762, + "username" : "service-account-producer", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "serviceAccountClientId" : "producer", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test", "producer" ], + "notBefore" : 0, + "groups" : [ ] + }, { + "id" : "7a51406b-d6d8-4c77-9b8a-135a2f07d8d5", + "createdTimestamp" : 1677053286393, + "username" : "service-account-rabbitmq-proxy-client", + "enabled" : true, + "totp" : false, + "emailVerified" : false, + "serviceAccountClientId" : "rabbitmq-proxy-client", + "credentials" : [ ], + "disableableCredentialTypes" : [ ], + "requiredActions" : [ ], + "realmRoles" : [ "default-roles-test" ], + "notBefore" : 0, + "groups" : [ ] + } ], + "scopeMappings" : [ { + "client" : "producer", + "roles" : [ "producer" ] + }, { + "clientScope" : "rabbitmq.read:*/*", + "roles" : [ "producer" ] + }, { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + }, { + "clientScope" : "rabbitmq.configure:*/*", + "roles" : [ "producer" ] + }, { + "clientScope" : "rabbitmq.tag:management", + "roles" : [ "rabbitmq-management" ] + }, { + "clientScope" : "rabbitmq.write:*/*", + "roles" : [ "producer" ] + }, { + "clientScope" : "rabbitmq.tag:administrator", + "roles" : [ "rabbitmq.tag:administrator" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "bd6c76be-d33d-43d6-9cbb-965df4f0c025", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "80023652-2709-4646-9367-b6114aa73bae", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/test/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/test/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "ebcf72c5-f58a-48cb-a6fb-db44e8735d7e", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "e5484264-82ff-46df-b38e-d5456439f413", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "f32cd0e1-5b78-412a-ba07-6ad2a9aeb007", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "c5be3c24-0c88-4672-a77a-79002fcc9a9d", + "clientId" : "mgt_api_client", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "LWOuYqJ8gjKg3D2U8CJZDuID3KiRZVDa", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "saml.force.post.binding" : "false", + "saml.multivalued.roles" : "false", + "frontchannel.logout.session.required" : "false", + "post.logout.redirect.uris" : "+", + "oauth2.device.authorization.grant.enabled" : "false", + "backchannel.logout.revoke.offline.tokens" : "false", + "saml.server.signature.keyinfo.ext" : "false", + "use.refresh.tokens" : "true", + "oidc.ciba.grant.enabled" : "false", + "backchannel.logout.session.required" : "true", + "client_credentials.use_refresh_token" : "false", + "require.pushed.authorization.requests" : "false", + "saml.client.signature" : "false", + "saml.allow.ecp.flow" : "false", + "id.token.as.detached.signature" : "false", + "saml.assertion.signature" : "false", + "client.secret.creation.time" : "1652084304", + "saml.encrypt" : "false", + "saml.server.signature" : "false", + "exclude.session.state.from.auth.response" : "false", + "saml.artifact.binding" : "false", + "saml_force_name_id_format" : "false", + "acr.loa.map" : "{}", + "tls.client.certificate.bound.access.tokens" : "false", + "saml.authnstatement" : "false", + "display.on.consent.screen" : "false", + "token.response.type.bearer.lower-case" : "false", + "saml.onetimeuse.condition" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "33fd8faf-3ea6-4669-beea-45b9655cf6ab", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "aae7e2aa-72e7-4d29-ae68-a66b846d62ab", + "name" : "aud", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "false", + "access.token.claim" : "true", + "included.custom.audience" : "rabbitmq", + "userinfo.token.claim" : "false" + } + }, { + "id" : "f7e826de-e651-4080-8e97-feba46b8a0a2", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientId", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientId", + "jsonType.label" : "String" + } + }, { + "id" : "545a1d71-5dc8-491c-bf7b-1c672d50e606", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "rabbitmq.tag:administrator", "rabbitmq.tag:management", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "3e96bddd-95f9-4277-b3ad-f8f6f5d5bb59", + "clientId" : "producer", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "saml.force.post.binding" : "false", + "saml.multivalued.roles" : "false", + "frontchannel.logout.session.required" : "false", + "post.logout.redirect.uris" : "+", + "oauth2.device.authorization.grant.enabled" : "false", + "backchannel.logout.revoke.offline.tokens" : "false", + "saml.server.signature.keyinfo.ext" : "false", + "use.refresh.tokens" : "true", + "oidc.ciba.grant.enabled" : "false", + "backchannel.logout.session.required" : "false", + "client_credentials.use_refresh_token" : "true", + "require.pushed.authorization.requests" : "false", + "saml.client.signature" : "false", + "saml.allow.ecp.flow" : "false", + "id.token.as.detached.signature" : "false", + "saml.assertion.signature" : "false", + "client.secret.creation.time" : "1652081901", + "saml.encrypt" : "false", + "saml.server.signature" : "false", + "exclude.session.state.from.auth.response" : "false", + "saml.artifact.binding" : "false", + "saml_force_name_id_format" : "false", + "acr.loa.map" : "{}", + "tls.client.certificate.bound.access.tokens" : "false", + "saml.authnstatement" : "false", + "display.on.consent.screen" : "false", + "token.response.type.bearer.lower-case" : "false", + "saml.onetimeuse.condition" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "72928dd9-10c9-4049-bfa7-4cc05e650f46", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "4c3b3c28-795f-4056-a854-5cf119b36266", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientId", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientId", + "jsonType.label" : "String" + } + }, { + "id" : "56b7571c-3226-4c92-8615-c99b265a42fc", + "name" : "aud", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "false", + "access.token.claim" : "true", + "included.custom.audience" : "rabbitmq", + "userinfo.token.claim" : "false" + } + }, { + "id" : "4ca73107-b26b-46ee-985b-d2dcc099f21c", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + }, { + "id" : "26e5243a-3127-4528-9a54-8af324ac2392", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "d52cc6cb-08a1-4c2b-bf06-61f234a419d1", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "rabbitmq.read:*/*", "rabbitmq.write:*/*", "roles", "rabbitmq.configure:*/*" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "a57c9f6a-8b64-47dc-af53-d6ccc2d4aa60", + "clientId" : "rabbitmq", + "name" : "", + "description" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "oidc.ciba.grant.enabled" : "false", + "oauth2.device.authorization.grant.enabled" : "false", + "backchannel.logout.session.required" : "true", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "e64b05d1-0d1c-4294-85f9-52ae098ecf1f", + "clientId" : "rabbitmq-client-code", + "name" : "", + "description" : "", + "rootUrl" : "http://localhost:15672/", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "http://localhost:15672/*" ], + "webOrigins" : [ "" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "saml.force.post.binding" : "false", + "saml.multivalued.roles" : "false", + "frontchannel.logout.session.required" : "false", + "post.logout.redirect.uris" : "+", + "oauth2.device.authorization.grant.enabled" : "false", + "backchannel.logout.revoke.offline.tokens" : "false", + "saml.server.signature.keyinfo.ext" : "false", + "use.refresh.tokens" : "true", + "oidc.ciba.grant.enabled" : "false", + "backchannel.logout.session.required" : "false", + "client_credentials.use_refresh_token" : "false", + "require.pushed.authorization.requests" : "false", + "saml.client.signature" : "false", + "saml.allow.ecp.flow" : "false", + "id.token.as.detached.signature" : "false", + "saml.assertion.signature" : "false", + "client.secret.creation.time" : "1652171962", + "saml.encrypt" : "false", + "saml.server.signature" : "false", + "exclude.session.state.from.auth.response" : "false", + "tls-client-certificate-bound-access-tokens" : "false", + "saml.artifact.binding" : "false", + "saml_force_name_id_format" : "false", + "acr.loa.map" : "{}", + "tls.client.certificate.bound.access.tokens" : "false", + "saml.authnstatement" : "false", + "display.on.consent.screen" : "false", + "token.response.type.bearer.lower-case" : "false", + "saml.onetimeuse.condition" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "e6905c3e-7ace-4b4f-9244-0f20a86da8ef", + "name" : "aud", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "false", + "access.token.claim" : "true", + "included.custom.audience" : "rabbitmq", + "userinfo.token.claim" : "false" + } + }, { + "id" : "548a2e70-5a2b-4959-8c72-97f6455ce478", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "extra_scope", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "923edb6d-2188-4f23-a547-7e372d9cb5eb", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "user_name", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "rabbitmq.tag:administrator", "profile", "roles", "rabbitmq.tag:management", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "c265f3db-ed3a-4898-8800-af044b3c30f5", + "clientId" : "rabbitmq-proxy-client", + "name" : "", + "description" : "", + "rootUrl" : "", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "nt6pmZMeyrgzYgkg2MLgZQZxLveRMW5M", + "redirectUris" : [ "http://0.0.0.0:4180/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : true, + "publicClient" : false, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "client.secret.creation.time" : "1677053168", + "post.logout.redirect.uris" : "+", + "oauth2.device.authorization.grant.enabled" : "false", + "backchannel.logout.revoke.offline.tokens" : "false", + "use.refresh.tokens" : "true", + "tls-client-certificate-bound-access-tokens" : "false", + "oidc.ciba.grant.enabled" : "false", + "backchannel.logout.session.required" : "true", + "client_credentials.use_refresh_token" : "false", + "acr.loa.map" : "{}", + "require.pushed.authorization.requests" : "false", + "display.on.consent.screen" : "false", + "token.response.type.bearer.lower-case" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "protocolMappers" : [ { + "id" : "e1c2389a-c5ca-4a81-a5c2-67f919f2368d", + "name" : "Client ID", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientId", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientId", + "jsonType.label" : "String" + } + }, { + "id" : "54b12841-4524-4b8a-8dc0-bb6f9044e11d", + "name" : "Client IP Address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientAddress", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientAddress", + "jsonType.label" : "String" + } + }, { + "id" : "a5c803da-af15-4fc8-ad7f-a4a900f0703b", + "name" : "Client Host", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usersessionmodel-note-mapper", + "consentRequired" : false, + "config" : { + "user.session.note" : "clientHost", + "userinfo.token.claim" : "true", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "clientHost", + "jsonType.label" : "String" + } + }, { + "id" : "958d4a83-d5b3-4cca-af3e-fde9f9328eec", + "name" : "aud", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-mapper", + "consentRequired" : false, + "config" : { + "included.client.audience" : "rabbitmq-proxy-client", + "id.token.claim" : "true", + "access.token.claim" : "true", + "included.custom.audience" : "rabbitmq" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "rabbitmq.tag:administrator", "profile", "offline_access", "microprofile-jwt" ] + }, { + "id" : "09de26c0-0a1b-4e5f-8724-23af07a0e54a", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "16f67c5c-f86b-4334-93f4-fd26356cbb24", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/test/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/test/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "26e7deed-9c26-4a19-88fa-845bec2e5909", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "ec4e76a3-8597-41d4-aa7c-e4e1fee6a01a", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${profileScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "db04690a-de25-4627-8e0c-78a018e86ce8", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "e5c72df5-7fa9-43c6-8f13-c7c2d73fe89a", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "5856da2a-7aa0-446c-be48-22112783e322", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + }, { + "id" : "5a7a208b-70eb-4f8f-b8ff-2115a615d696", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "b1cad309-90cd-4fed-8e62-c05dc4649b99", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "b3a08e61-6e08-4aa3-aa71-212bc13bff5d", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "bc59bb88-2cae-4c60-b09f-3c18fced603f", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "da32f964-8b0a-4cef-babc-8b90f31b20a7", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "eb6b8e8c-1e03-497a-80b4-3e9c26a86d9a", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "334e47b2-5f74-4668-b04e-9ab55513c146", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "long" + } + }, { + "id" : "7dad1c25-6b18-4571-8d92-bfd698c5b94b", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "bf52c928-4d33-4c14-8e61-969b17bed2a5", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "3a03eb21-7c20-4150-87f2-ca94c9df601c", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "431c9682-e4ba-4348-9d07-f8d5415ca98b", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "b2ced9e2-289f-44b0-8567-5218a2eee3e6", + "name" : "rabbitmq.read:*/*", + "description" : "read all", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + }, { + "id" : "46f5e514-9283-4f03-b2af-a7da506f0cbc", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "a4ffacea-34a8-4eb2-961f-af78e50b1140", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${addressScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "791b8544-4659-4d61-8fb8-8a18a687648d", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "7a2981c1-d606-43f6-acbf-76a8124e59b7", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "63a285df-f6d0-4e06-9f16-d4a578fce8bf", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + }, { + "id" : "1665df09-6855-420b-a649-0f0afe054b51", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "multivalued" : "true", + "userinfo.token.claim" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "b9c5af5d-59f3-445b-b899-c6574bc6191b", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${phoneScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "e945c389-e953-431b-b3b4-882a50a8054e", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + }, { + "id" : "52b07524-1521-48d6-be23-779f8e1f8a67", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "4dfc0d4d-654d-4e1c-8b58-64a0e1126a19", + "name" : "rabbitmq.configure:*/*", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + }, { + "id" : "93d154c5-e9fe-49ff-bca8-bc55a141a31e", + "name" : "rabbitmq.tag:management", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + }, { + "id" : "cc214fa3-0a7f-4390-9c4a-8ae14512e4a4", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false", + "consent.screen.text" : "" + }, + "protocolMappers" : [ { + "id" : "e2ac8ddb-9c19-4088-bc72-c4176e0fac3f", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { } + } ] + }, { + "id" : "53b1a2b2-085e-4e36-bb81-c88e8d846439", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${emailScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "e1a850d9-6372-4521-8d0d-acee25245c90", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + }, { + "id" : "43a7220e-d94d-43e6-a5e7-1a12dbbb4460", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "2b745afc-cb92-4ac3-b314-8ef1d638b4b1", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${rolesScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "2cac7c2c-7c9f-44e6-a76e-b8d3fad627ea", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "91c4a9bd-a9b9-402b-9eb6-762362d18c6b", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "8b53c714-89cf-4cfd-ac76-1b45bd841b58", + "name" : "acr", + "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "6381b445-4f37-434e-b982-c34a6048913b", + "name" : "acr loa level", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-acr-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + } ] + }, { + "id" : "3331893c-c67c-4146-b00a-b4862200628c", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "863078ec-d37c-46fc-a70e-3fe6340fbeec", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "2010b133-4bfe-4f5f-8d1a-33b2a7ad2e60", + "name" : "rabbitmq.write:*/*", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + }, { + "id" : "f6e6dd62-22bf-4421-910e-e6070908764c", + "name" : "rabbitmq.tag:administrator", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true" + } + } ], + "defaultDefaultClientScopes" : [ "role_list", "profile", "email", "roles", "web-origins", "acr" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection" : "1; mode=block", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "identityProviders" : [ ], + "identityProviderMappers" : [ ], + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "72a90aea-e732-467d-ade9-34e73c993209", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "5ed9083b-fdb5-4fc5-97b5-0b31189f8ad2", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "4f81b0b9-6a42-4128-b7a1-4a814acf5875", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "2c06a7de-50eb-439f-b592-6bbda10d6af3", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-full-name-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "saml-user-property-mapper", "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-address-mapper" ] + } + }, { + "id" : "693f0625-c453-40c0-b38e-80b7b7deaefa", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "7c971805-14f2-4eb0-b2af-90c2db4c2e41", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "e4376815-05e5-4675-9b11-6e18d5712849", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "saml-user-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "oidc-usermodel-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-full-name-mapper" ] + } + }, { + "id" : "bbadf932-a286-4841-be1b-ed845e2131cb", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "307ede55-7647-498c-b1ba-4be80fb609cc", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEogIBAAKCAQEAx5RCXJqU+e2646hYCduHBANJvpYN3Uv1gq15LjTlIXYqBCfm4SLUX1y1lOKDoOVl5j/flAgdgF4I9P3G4drc36NuaaocQguu3xWOsG9UZheiDD4wJANC7F6FMdWqSiBySA+EXyQ2zkoUBkNxKecqWhqVmaY9IVyxbQhdXsKQH0hBnvd3NQaem0RXuUeadUK5TGI9VqTe96sN7lLdE+T94n5cubqtBbc14kZ6YZsi1Pa+2xe/ZsDlXgMGAqTxgSy5l6cnxzxE+ndAp+pIR83BxfRFijo0LNKxyiZQ0X+QZ4Y3sTGadLCZzr6R/N/1QQnhOTXmTsVyNdmhQMr6A7lBFwIDAQABAoIBAAP7tYdbnnWOhRheF8O6mes+lY40OHqeNXwyMiT18UzFqvkCQt1jcJGmrAkYrD/d1DbQN4ogz1Xsiok5N2ryj033WRDK0F2RFiBlsb9buXeAKT/NTfCqD//fsxDXjtqD40QE60Nq2Z0sZVHqrquDbZj2xt2WL8omq3Pdot9tSqsVIQMbRIfH+I9+9kQ8Ob7t423I06AFiXJg5h3qjLx1jP6qQWsC4ippY6QmUve/d3PWqSd4GQ4sb2KQKvfT1VU4HPvIQdf+OurXaF/lPR+6XDU3RmA1qY61JS0O0ul+jTUGUHRxtgI51IU16+jcKiAzjWZ53HI9jLODP6gzyn+KC2ECgYEA/2SN4sNHM8aL/i7hdcbq+c+UFKK5AyWjewbMWGC2ZCzEzW/zqn2lmQHZK7hDAuAhvnEh6rvxxFKQw2PXb9N9IGIaAEJmGrJYg5QaLspNs5/4a3+GZh8lfZgwaEBetazrIFSOrVFhYb/pRz6m2x7oKNIpVXXUXdNr0DD4mDAwLqUCgYEAyA272k5rlTXj2IX/MvAtAuD2l7R6DfDirolzNdLVEXKLWpZwqdAuLWhDNGvNtmeAQcIuGFUvHhv5ZdnAwOKjbJsVDmr8vUCegCZJPuEGuOXjqZq+a1a84lhSSyWWiiz/yuIVh2Bnu8TD2Xb3igNFa0ipWga9nfZm7usifVumQAsCgYA/z+kfyrkkt6xM83vECNK3XmVajpn5rlLdr4IpZujLuN/nkNxqMgDJbUvM/7pGoqfrxKq70wAClLq1B2JR/57ZE4n5nJ2UeBFjtwKfxE6L3iGdAn0bURb1/avCsKq5bB5Hsrj/l0DkwqXP9liMkXlikbhgMRPB6cybdVD3/bpcPQKBgFxZIqeC3dzSNKymJokwH8cdbBfZwyIeWbXyU1G8UoksVDHsEnQBWt+xKpzPSvXxz14LslWfNRH0HeurWnRv6rre2BiAwMzoQIKtqdAx9nVyAecwPMi2EJl35f00i8qbPTU9qmyEzz35deM0LM7z9Z6xuyOIyw1ZSmjt+Ezf+t3DAoGAB7rT8E7lc036+L1RzKF+T8XwXReHcDkXhKt5V/RQBzRL5GY2mqQQ1rb037KlGXRHAwPkQqmYpMhY9ccRF5UqA05IT/KApvc36m7DAXPaNy1CwZHrr0l3rR6fhpUvtgrt1uyCXvaLJxUAd/5MTw2ffqOsMSxiuRCrpUC+dxCXtG8=" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIIClzCCAX8CBgGAmWyoEzANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTIyMDUwNjEyNDkzNloXDTMyMDUwNjEyNTExNlowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMeUQlyalPntuuOoWAnbhwQDSb6WDd1L9YKteS405SF2KgQn5uEi1F9ctZTig6DlZeY/35QIHYBeCPT9xuHa3N+jbmmqHEILrt8VjrBvVGYXogw+MCQDQuxehTHVqkogckgPhF8kNs5KFAZDcSnnKloalZmmPSFcsW0IXV7CkB9IQZ73dzUGnptEV7lHmnVCuUxiPVak3verDe5S3RPk/eJ+XLm6rQW3NeJGemGbItT2vtsXv2bA5V4DBgKk8YEsuZenJ8c8RPp3QKfqSEfNwcX0RYo6NCzSscomUNF/kGeGN7ExmnSwmc6+kfzf9UEJ4Tk15k7FcjXZoUDK+gO5QRcCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAp2oy7aEJWJ1/BBUlFArzHqXZ/aYzuRUdyCTQn6+tjRsB9IrPwRI4p9sN4mCl0EUDvureNxCiY9XBHWmEteb5n952XWcSWi6tzAj5BQL4LHIFFzMLHr1+HYpmMwYHFgmR9MhoKFINEEGpOvCRokurEN2uU/tNcBX5HGnEWRc5ZNPRwQcJggnHHWxAmuNKIf73pPAECcffrTJO6cePt++TaU2j2qA6S5A/p/0Za10EtKcNeL1EIvwuFxewBjJQjXEqvmN4VlcVadj+pQ7AwtzujdtCuHvO/9zxRo5xq9KFl/VVMk4fUvwKA+vjcMwjDn2qXZwKmslX6YkV18gbmnSCOg==" ], + "priority" : [ "100" ] + } + }, { + "id" : "1ae80491-123b-4d2d-8018-7ceb6971d07d", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "e43bf5a9-a6eb-46e6-a529-2174e96536fd" ], + "secret" : [ "2cQajcd-396pH2TSx6TC-Q" ], + "priority" : [ "100" ] + } + }, { + "id" : "b7b404ce-f1db-4fba-9037-d43bbd5fa584", + "name" : "hmac-generated", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "2a9eb5b7-3df3-4fe8-980b-93808456c392" ], + "secret" : [ "ghLjXQyrSvgpx1wi2-YYgGSOUH2-FteA8BN_FujK8wywdQ-LiMd-WDfnw-F6GexWHqNrv95VRjHzvlojRbDluQ" ], + "priority" : [ "100" ], + "algorithm" : [ "HS256" ] + } + }, { + "id" : "f7ed445f-b2bd-4cbd-9985-eba8b36ed733", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEAjn/EV027lKv/NvYuCxCqUrAsVKTHpKLyFZCeJfrMcGn9XDTjMMYVx5/2wguPJFpX+nUzFSCjGuejRWamVFOoHiaXB+mrdPoAZbBNrgdeqwb2+7+G0iqsmU+Lfi/pxsubTTLZIZB1PBfV/4DmWz3vRU+uapiHo5pn+h0mbeOCLltuZmygiKlkzpTLfZxdmSXMqCwZv9J6Zdvrpio88Ca2F1dD4w3bPWvcbLmftAkpByucyHlo0v3jLPbq9LQF7fRD3WqHDGgQjLFJJKvN16tTnVkLWzFE4tumsQirCp8MoAuC3fKa5X5AKjZRarcrml9bmnFId+VGs764FY8THVuqtQIDAQABAoIBAC2FBOw6iYxRcSJWe5jPRwlI+7BCPwAJiTl4S/yn4/yY7vUwc86VyElPgRy1YpFjRq1cGOhL1651hkNaKhHP82lGPIKrkzi3z76sFfRcqFIL1IPQx7mFJkOHFHFHzu7RBZgggRnmsnxyxMpsm6lT3IYQkQ++D74kScis3STHQ0OZOVGkx9Sbznx1+i2rc7QUkWy8b7G8/ByVJsAu2SBLfbQ4wFAhJKtr+uDP6Tt2Cyn4GpzE68B5cA7htJI49uVvbGTBLInH5PXejXKyLfEJjbH2brx9hVStmaaV/sD1hroZ0sjhM54MamctPq2o0H33S2WFZJaUXwDWHzOSyUmY5SECgYEAxTJmhuCYz/P2AZP4DGDNwFHLar1K719m+RxUu0ndIDKeGhBtX3oSQXaKQS+JM+dmbF3M7ub18OgCHF+raPXAE6en1tdh5ZQJbRQEX9dnaXVpjCPFciZMgP0eJ1CpMuKo7qb3IXv7YtcN6pFjMNchz3MyVqbNBFN8ocQWoe4VJu0CgYEAuP3Z45ce6XI+7274yTXMEHqmCZU8krKanmppgIj39iJGbkEBo9QSB32XhG7dU0tWJhj4QtQcMKFCMcqckw9Jetyb68J2vreCVti9CqSrSLL4VFhMzVdoRPflzXB29gdJTe4TxBAiSryVICVblW3giM4UWhzNAuJOC8f5r1xrkekCgYBmKyXJreYeoBSOXr6+kw0nHnnZFLgVa4Vrfc08uBlUTEVz1Z0FQbbhqewZt+pLNRHxBWxfPtSf+2TUlJC3sdPRmySvgCodi2SS2jMmAPF4RzfnPsVWzhcHIZ2U2wq+7YZ/F4ylEZp+bFOue6M7s8q1s8aZ9JP2MNc67OCZB0R4RQKBgQC3YfZIVfuvwbA/3ItFs03KnDrSTx2P8vuxxJ03bRAZ8BpPm6OLi2QgBtFX2CsRMiKBe8lHPkt/rawX/dk/My1NXTo3+TuLjhDoFM05qsmdNMVVn37rJBXaIMCu6ikTdV+moDb56mCEI/PUvRPPyu+FznyAZAKbNEnYBfIvc3ezWQKBgEIw8dPvzmmbGvcb1K56VlyKTcejY0tPk8spRd+BKpi+VXlhV+1xLVmyB6lzveFm+0Da2arFGxFKgi/PfT5mXfheGyX2dxMbK5cMkYg7R+pHBbm/0XXAdQnaGZ4wz0O1R/DC8t0ZDp4wSlb3fVJpQoEKkqMY9bK2H4DvToOvLFck" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIIClzCCAX8CBgGAmWypGDANBgkqhkiG9w0BAQsFADAPMQ0wCwYDVQQDDAR0ZXN0MB4XDTIyMDUwNjEyNDkzNloXDTMyMDUwNjEyNTExNlowDzENMAsGA1UEAwwEdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI5/xFdNu5Sr/zb2LgsQqlKwLFSkx6Si8hWQniX6zHBp/Vw04zDGFcef9sILjyRaV/p1MxUgoxrno0VmplRTqB4mlwfpq3T6AGWwTa4HXqsG9vu/htIqrJlPi34v6cbLm00y2SGQdTwX1f+A5ls970VPrmqYh6OaZ/odJm3jgi5bbmZsoIipZM6Uy32cXZklzKgsGb/SemXb66YqPPAmthdXQ+MN2z1r3Gy5n7QJKQcrnMh5aNL94yz26vS0Be30Q91qhwxoEIyxSSSrzderU51ZC1sxROLbprEIqwqfDKALgt3ymuV+QCo2UWq3K5pfW5pxSHflRrO+uBWPEx1bqrUCAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAThu6AxY4bqRbl4RtPH5Xcm033xwLssXmVXh01NuQUfowtkHi77lGUWXBmvwY6UIKC4D4eYrGVYVRTjHUEknmLrxSzUKi4g38kax4pBfwWWstWyVyo89dl7hA5ZlzdZ+SFB4HasGcXdhVFG2dwVvx6lnfxBIWZkgy5GAtIOpK4oIJOIutTiR1yuku4a9zkk6yumsxTKivAs1UMsvQXzcFSUDIrdj0vfdCAB6SvjpYvf8d0wO31bb+t3vWblv29RNV4qbuA/CZkrvWZZXZ+bal0qZd06Z+Hbc4iBgPSHs/HjyAJ8xac3ljg0IWolvZxRkFBR4VSB3pgCUKxf3V4mgbPw==" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + }, { + "id" : "66a592ec-8657-4f53-8870-1e1693ff266c", + "name" : "rsa", + "providerId" : "rsa", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpAIBAAKCAQEA2dP+vRn+Kj+S/oGd49kq6+CKNAduCC1raLfTH7B3qjmZYm45 yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhKIdcIWadhqDzdtn1hj/22iUwrhH0b d475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2B9q9KFBmo4Ahh/6+d4wM1rH9kxl0 RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF2cr3wQwCfF1qVu4eAVNVfxfy/uEv G3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgGQAvkknWitpRK8KVLypEj5WKej6CF 8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7VwIDAQABAoIBAFsB5FszYepa11o3 4zSPxgv4qyUjuYf3GfoNW0rRGp3nJLtoHAIYa0CcLX9kzsQfmLtxoY46mdppxr8Z 2qUZpBdRVO7ILNfyXhthdQKI2NuyFDhtYK1p8bx6BXe095HMcvm2ohjXzPdTP4Hq HrXAYXjUndUbClbjMJ82AnPF8pM70kBq7g733UqkdfrMuv6/d95Jiyw4cC7dGsI3 Ruz9DGhiAyCBtQ0tUB+6Kqn5DChSB+ccfMJjr6GnCVYmERxEQ5DJCTIX8am8C6KX mAxUwHMTsEGBU6GzhcUgAwUFEK3I9RptdlRFp7F8E/P0LxmPkFdgaBNUhrdnB7Y4 01n1R1kCgYEA/huFJgwVWSBSK/XIouFuQrxZOI9JbBbdmpFT7SBGCdFg26Or9y7j +N5HE7yuoZ9PkBh17zzosZdsJhGocRYvO0LSq8cXvKXKCwn2fTMM7uJ/oQe68sxG cF/fC0M/8LvRESWShH920rrERu0s161RuasdOPre0aXu7ZQzkQ68O6MCgYEA23NO DHKNblBOdFEWsvotLqV8DrIbQ4le7sSgQr56/bdn9GScZk2JU0f+pqzpiGUy9bIt 6uujvt5ar0IvpIQVdjf3dbp6Fy+Dwhd4yTR4dMdDECest7jL++/21x8Y0ywFhBIK yEd+QxpOLXP6qaSKTGxL2rnTXRjl8/g629xQPL0CgYEAkNNOh+jLIgjxzGxA9dRV 62M91qaTyi8eDkJV+wgx4taaxZP7Jt5qwCSvjegz/5m01wOZ88hbNxx+XxQhVJK4 SKZFO/I07Sfwh2oeOi0maeBdrYGiY09ZtiJuFRU3FBV3irZHU4zyRBh+VY5HyITX 12JXPWp+JC7WhkG5QiuLzNECgYEA15OBzICLpx6Es4clAVT6JaSzJcyZM9MyyuOl e2ubbrpJCK/9ZBIvIPzMj/e0wiSH1wzeRrSM+ud7tkcSfk6ytptsIN67KSOoD3b3 VNCStEU7ABe5eBG1cRzeI52MyYWpNYBzzyNMSacBvWz9hMD6ivCn44pAtGfNHclw KKNYvxECgYBOamf25md9Jy6rtQsJVEJWw+8sB4lBlKEEadc5qekR7ZQ0hwj8CnTm WOo856ynI28Sog62iw8F/do/z0B29RuGuxw+prkBkn3lg/VQXEitzqcYvota6osa 8XSfaPiTyQwWpzbFNZzzemlTsIDiF3UqwkHvWaMYPDf4Ng3cokPPxw==" ], + "certificate" : [ "MIICmDCCAYACCQC7YJWOo6LVaDANBgkqhkiG9w0BAQsFADAOMQwwCgYDVQQDDANq d3QwHhcNMjIwNTA2MTQzNjQ5WhcNMjIwNjA1MTQzNjQ5WjAOMQwwCgYDVQQDDANq d3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDZ0/69Gf4qP5L+gZ3j 2Srr4Io0B24ILWtot9MfsHeqOZlibjnIOX5eaAr0I2YdeSGj2q+aF2Sx3MNWx14O WEoh1whZp2GoPN22fWGP/baJTCuEfRt3jvmGUpyyJn6jL+x2AaADO+aZNCZ0yoRe pXYH2r0oUGajgCGH/r53jAzWsf2TGXRG8wAot751qggciOiTyEI7hwpBATD8SsF7 hIXZyvfBDAJ8XWpW7h4BU1V/F/L+4S8bdDvHTTk/dNwr5BxiAnFq/eWJy1KLl3JY uAZAC+SSdaK2lErwpUvKkSPlYp6PoIXyerfS62fXkVCDQmQeirPCIKqp56fwYg+1 4jtXAgMBAAEwDQYJKoZIhvcNAQELBQADggEBACJlWtWnQqepYiFCijVgy/eM5KL0 rFZOZ6HNefoJTrYY1QYZrWxRz3M4u9JpUy4fBvGHxElBElcr3fXLXDytH9EwMJm1 E5x3o3qkQyWdXYGW6ZF58dklcJTdejOxEO373qpywVwbCFGiuIt7s5v4v+r2HOg3 D4elb2bqxmRim04xIkVZufKo+h6a8dBb5JEU3UaxyGDBR0IdyjhyBo1+HhH+RqZs xQhQ7DhlIGWUYZNCu13fb1GNSMiNqspKnMpFdQ4Bfpsb7vOeEK+aqJjCKcYbuGa6 BiwBjbKYyEF5r01Tob50dcVPfIGOqO0lQ3IsV31n9LSoAAtaVqioPK1rvDo=" ], + "active" : [ "true" ], + "priority" : [ "101" ], + "enabled" : [ "true" ], + "algorithm" : [ "RS256" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "d07a624b-8e59-43fa-8437-6a15d8ed75fb", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false + } ] + }, { + "id" : "512a7da1-b2fa-4081-b724-dcb777dbcf55", + "alias" : "Authentication Options", + "description" : "Authentication options.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "basic-auth", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "basic-auth-otp", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "72e840a1-ad4c-46da-b614-f62f632ce941", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "c7c7cdc9-027c-4bc9-9c54-7b021d0c2589", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "29397d8b-f6e1-4ec9-afac-565e3652a55e", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "852b85a0-f8db-406c-82c4-8becac69b745", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false + } ] + }, { + "id" : "13b6e45c-2a8c-48da-903f-edb567dcc2a9", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "70315d81-39f1-465d-8498-de28b87ddb34", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false + } ] + }, { + "id" : "a2852c66-3968-4f81-938c-85e41707e48d", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "b5959d71-2358-4bfc-90fc-d8bea212894e", + "alias" : "browser", + "description" : "browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "identity-provider-redirector", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 25, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "forms", + "userSetupAllowed" : false + } ] + }, { + "id" : "b0ab14fa-7321-4158-8e0d-c49a2f3e25ae", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-secret-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-x509", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "33e12607-e441-4d8f-b9fe-db29eff9aab7", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "bc63c1bc-cb85-4f3c-ba1e-3d270d85244b", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "525b6455-c328-4166-a05d-af8e47b4fbd6", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false + } ] + }, { + "id" : "1cba19e3-bef5-4b3a-b156-3539b81a20fd", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "45ce11b0-6595-42e3-be69-3c1d052371d7", + "alias" : "http challenge", + "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "no-cookie-redirect", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Authentication Options", + "userSetupAllowed" : false + } ] + }, { + "id" : "8bc948ff-c5a6-4e5e-998c-6a0e77e7b16b", + "alias" : "registration", + "description" : "registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "registration form", + "userSetupAllowed" : false + } ] + }, { + "id" : "58022df6-ad48-4b0e-9b77-663ea0a19e9f", + "alias" : "registration form", + "description" : "registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-profile-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-password-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-recaptcha-action", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 60, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "c45b5402-d7cb-4973-87c9-36a8fba5af25", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-credential-email", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 40, + "autheticatorFlow" : true, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "9cc342be-8b91-4559-8da5-d259c9a87903", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "8f1fdb89-ccc1-4238-bcf6-3c7f364ee017", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "6835b816-87ad-46a4-a6cd-d3e4650cb7b4", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "terms_and_conditions", + "name" : "Terms and Conditions", + "providerId" : "terms_and_conditions", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + }, { + "alias" : "delete_account", + "name" : "Delete Account", + "providerId" : "delete_account", + "enabled" : false, + "defaultAction" : false, + "priority" : 60, + "config" : { } + }, { + "alias" : "update_user_locale", + "name" : "Update User Locale", + "providerId" : "update_user_locale", + "enabled" : true, + "defaultAction" : false, + "priority" : 1000, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "attributes" : { + "cibaBackchannelTokenDeliveryMode" : "poll", + "cibaExpiresIn" : "120", + "cibaAuthRequestedUserHint" : "login_hint", + "oauth2DeviceCodeLifespan" : "600", + "clientOfflineSessionMaxLifespan" : "0", + "oauth2DevicePollingInterval" : "5", + "clientSessionIdleTimeout" : "0", + "parRequestUriLifespan" : "60", + "clientSessionMaxLifespan" : "0", + "clientOfflineSessionIdleTimeout" : "0", + "cibaInterval" : "5", + "realmReusableOtpCode" : "false" + }, + "keycloakVersion" : "20.0.3", + "userManagedAccessAllowed" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} diff --git a/projects/TestApplications/OAuth2/keycloak/rabbitmq.conf b/projects/TestApplications/OAuth2/keycloak/rabbitmq.conf new file mode 100644 index 0000000000..207106a14f --- /dev/null +++ b/projects/TestApplications/OAuth2/keycloak/rabbitmq.conf @@ -0,0 +1,7 @@ +auth_backends.1 = rabbit_auth_backend_oauth2 + +auth_oauth2.resource_server_id = rabbitmq +auth_oauth2.preferred_username_claims.1 = username +auth_oauth2.additional_scopes_key = permissions +auth_oauth2.default_key = Gnl2ZlbRh3rAr6Wymc988_5cY7T5GuePd5dpJlXDJUk +auth_oauth2.signing_keys.Gnl2ZlbRh3rAr6Wymc988_5cY7T5GuePd5dpJlXDJUk = /etc/rabbitmq/signing-key.pem diff --git a/projects/TestApplications/OAuth2/keycloak/signing-key/signing-key.pem b/projects/TestApplications/OAuth2/keycloak/signing-key/signing-key.pem new file mode 100644 index 0000000000..a49f2f41e6 --- /dev/null +++ b/projects/TestApplications/OAuth2/keycloak/signing-key/signing-key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq +6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK +IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2 +B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF +2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG +QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7 +VwIDAQAB +-----END PUBLIC KEY----- diff --git a/projects/TestApplications/OAuth2/start-keycloak.sh b/projects/TestApplications/OAuth2/start-keycloak.sh new file mode 100755 index 0000000000..18cc936c99 --- /dev/null +++ b/projects/TestApplications/OAuth2/start-keycloak.sh @@ -0,0 +1,33 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly script_dir + +source "$script_dir/common.sh" + +readonly docker_name="$docker_name_prefix-keycloak" + +docker network inspect rabbitmq_net >/dev/null 2>&1 || docker network create rabbitmq_net +docker rm -f "$docker_name" 2>/dev/null || echo "[INFO] keycloak was not running" +echo "[INFO] running $docker_name docker image ..." + +readonly signing_key_file="$script_dir/keycloak/signing-key/signing-key.pem" +if [[ -f $signing_key_file ]] +then + OAUTH2_RABBITMQ_EXTRA_MOUNTS="${OAUTH2_RABBITMQ_EXTRA_MOUNTS:-} --volume $signing_key_file:/etc/rabbitmq/signing-key.pem" +fi + +# Note: +# Variables left un-quoted so that words are actually split as args +# shellcheck disable=SC2086 +docker run --detach --name "$docker_name" --net rabbitmq_net \ + --publish 8080:8080 \ + --env KEYCLOAK_ADMIN=admin \ + --env KEYCLOAK_ADMIN_PASSWORD=admin \ + --mount type=bind,source="$script_dir/keycloak/import/,target=/opt/keycloak/data/import/" \ + $OAUTH2_RABBITMQ_EXTRA_MOUNTS \ + quay.io/keycloak/keycloak:20.0 start-dev --import-realm diff --git a/projects/TestApplications/OAuth2/start-rabbit.sh b/projects/TestApplications/OAuth2/start-rabbit.sh new file mode 100755 index 0000000000..018d8aaf39 --- /dev/null +++ b/projects/TestApplications/OAuth2/start-rabbit.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly script_dir + +readonly mode="${MODE:-keycloak}" +readonly image_tag="${IMAGE_TAG:-3-management}" +readonly image="${IMAGE:-rabbitmq}" + +source "$script_dir/common.sh" + +readonly docker_name="$docker_name_prefix-rabbitmq" + +readonly signing_key_file="$script_dir/$mode/signing-key/signing-key.pem" +if [[ -f $signing_key_file ]] +then + OAUTH2_RABBITMQ_EXTRA_MOUNTS="${OAUTH2_RABBITMQ_EXTRA_MOUNTS:-} --volume $signing_key_file:/etc/rabbitmq/signing-key.pem" +fi + +readonly mount_rabbitmq_config="/etc/rabbitmq/rabbitmq.conf" +readonly config_dir="$script_dir/$mode" + +docker network inspect rabbitmq_net >/dev/null 2>&1 || docker network create rabbitmq_net +docker rm -f "$docker_name" 2>/dev/null || echo "[INFO] $docker_name was not running" + +echo "[INFO] running RabbitMQ ($image:$image_tag) with Idp $mode and configuration file $config_dir/rabbitmq.conf" + +# Note: +# Variables left un-quoted so that words are actually split as args +# shellcheck disable=SC2086 +docker run --detach --name "$docker_name" --net rabbitmq_net \ + --publish '15672:15672' --publish '5672:5672' ${OAUTH2_RABBITMQ_EXTRA_PORTS:-} \ + --volume "$config_dir/rabbitmq.conf:$mount_rabbitmq_config:ro" \ + --volume "$script_dir/enabled_plugins:/etc/rabbitmq/enabled_plugins" \ + --volume "$config_dir:/conf" ${OAUTH2_RABBITMQ_EXTRA_MOUNTS:-} "$image:$image_tag" diff --git a/projects/TestApplications/OAuth2/start-test.sh b/projects/TestApplications/OAuth2/start-test.sh new file mode 100755 index 0000000000..8cd9072ff3 --- /dev/null +++ b/projects/TestApplications/OAuth2/start-test.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +readonly mode=${MODE:-keycloak} +readonly client_id=producer + +if [[ $mode == keycloak ]] +then + readonly client_secret=kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn + readonly token_endpoint="http://localhost:8080/realms/test/protocol/openid-connect/token" + readonly scope="rabbitmq:configure:*/* rabbitmq:read:*/* rabbitmq:write:*/*" +else + readonly client_secret=producer_secret + readonly token_endpoint="http://localhost:8080/oauth/token" + readonly scope="" +fi + +dotnet run --Name "$mode" \ + --ClientId "$client_id" \ + --ClientSecret "$client_secret" \ + --Scope "$scope" \ + --TokenEndpoint "$token_endpoint" \ + --TokenExpiresInSeconds 60 diff --git a/projects/TestApplications/OAuth2/start-uaa.sh b/projects/TestApplications/OAuth2/start-uaa.sh new file mode 100755 index 0000000000..a6546c1e8b --- /dev/null +++ b/projects/TestApplications/OAuth2/start-uaa.sh @@ -0,0 +1,27 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly script_dir + +source "$script_dir/common.sh" + +readonly docker_name="$docker_name_prefix-uaa" + +readonly uaa_image_tag=${UAA_IMAGE_TAG:-75.21.0} +readonly uaa_image_name=${UAA_IMAGE_NAME:-cloudfoundry/uaa} + +docker network inspect rabbitmq_net >/dev/null 2>&1 || docker network create rabbitmq_net +docker rm --force "$docker_name" 2>/dev/null || echo "[INFO] $docker_name was not running" + +echo "[INFO] running ${uaa_image_name}:${uaa_image_tag} docker image" + +docker run --detach --name "$docker_name" --net rabbitmq_net \ + --publish 8080:8080 \ + --mount "type=bind,source=${script_dir}/uaa,target=/uaa" \ + --env UAA_CONFIG_PATH="/uaa" \ + --env JAVA_OPTS="-Djava.security.egd=file:/dev/./urandom" \ + "${uaa_image_name}:${uaa_image_tag}" diff --git a/projects/TestApplications/OAuth2/stop-keycloak.sh b/projects/TestApplications/OAuth2/stop-keycloak.sh new file mode 100755 index 0000000000..0dfd7f4d7d --- /dev/null +++ b/projects/TestApplications/OAuth2/stop-keycloak.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly script_dir + +source "$script_dir/common.sh" + +docker_stop keycloak diff --git a/projects/TestApplications/OAuth2/stop-rabbit.sh b/projects/TestApplications/OAuth2/stop-rabbit.sh new file mode 100755 index 0000000000..d04b956e50 --- /dev/null +++ b/projects/TestApplications/OAuth2/stop-rabbit.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly script_dir + +source "$script_dir/common.sh" + +docker_stop rabbitmq diff --git a/projects/TestApplications/OAuth2/stop-uaa.sh b/projects/TestApplications/OAuth2/stop-uaa.sh new file mode 100755 index 0000000000..3337ce17b2 --- /dev/null +++ b/projects/TestApplications/OAuth2/stop-uaa.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +readonly script_dir + +source "$script_dir/common.sh" + +docker_stop uaa diff --git a/projects/TestApplications/OAuth2/uaa/log4j2.properties b/projects/TestApplications/OAuth2/uaa/log4j2.properties new file mode 100644 index 0000000000..2e22eaa45d --- /dev/null +++ b/projects/TestApplications/OAuth2/uaa/log4j2.properties @@ -0,0 +1,30 @@ +status = error +dest = err +name = UaaLog + +property.log_pattern=[%d{yyyy-MM-dd'T'HH:mm:ss.nnnnnn}{GMT+0}Z] uaa%X{context} - %pid [%t] .... %5p --- %c{1}: %replace{%m}{(?<=password=|client_secret=)([^&]*)}{}%n + +appender.uaaDefaultAppender.type = File +appender.uaaDefaultAppender.name = UaaDefaultAppender +appender.uaaDefaultAppender.fileName = logs/uaa.log +appender.uaaDefaultAppender.layout.type = PatternLayout +appender.uaaDefaultAppender.layout.pattern = ${log_pattern} + +appender.uaaAuditAppender.type = File +appender.uaaAuditAppender.name = UaaAuditAppender +appender.uaaAuditAppender.fileName = logs/uaa_events.log +appender.uaaAuditAppender.layout.type = PatternLayout +appender.uaaAuditAppender.layout.pattern = ${log_pattern} + +rootLogger.level = debug +rootLogger.appenderRef.uaaDefaultAppender.ref = UaaDefaultAppender + +logger.UAAAudit.name = UAA.Audit +logger.UAAAudit.level = info +logger.UAAAudit.additivity = true +logger.UAAAudit.appenderRef.auditEventLog.ref = UaaAuditAppender + +logger.cfIdentity.name = org.cloudfoundry.identity +logger.cfIdentity.level = info +logger.cfIdentity.additivity = false +logger.cfIdentity.appenderRef.uaaDefaultAppender.ref = UaaDefaultAppender diff --git a/projects/TestApplications/OAuth2/uaa/rabbitmq.conf b/projects/TestApplications/OAuth2/uaa/rabbitmq.conf new file mode 100644 index 0000000000..b0a6d5b0f4 --- /dev/null +++ b/projects/TestApplications/OAuth2/uaa/rabbitmq.conf @@ -0,0 +1,15 @@ +auth_backends.1 = rabbit_auth_backend_oauth2 +management.oauth_enabled = true +management.oauth_client_id = rabbit_client_code +management.oauth_provider_url = http://localhost:8080/ + +amqp1_0.default_user = none + +auth_oauth2.resource_server_id = rabbitmq +auth_oauth2.additional_scopes_key = extra_scope +auth_oauth2.default_key = legacy-token-key +auth_oauth2.preferred_username_claims.1 = preferred_username +auth_oauth2.preferred_username_claims.2 = user_name +auth_oauth2.preferred_username_claims.3 = email + +auth_oauth2.signing_keys.legacy-token-key = /etc/rabbitmq/signing-key.pem diff --git a/projects/TestApplications/OAuth2/uaa/signing-key/signing-key.pem b/projects/TestApplications/OAuth2/uaa/signing-key/signing-key.pem new file mode 100644 index 0000000000..a49f2f41e6 --- /dev/null +++ b/projects/TestApplications/OAuth2/uaa/signing-key/signing-key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2dP+vRn+Kj+S/oGd49kq +6+CKNAduCC1raLfTH7B3qjmZYm45yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhK +IdcIWadhqDzdtn1hj/22iUwrhH0bd475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2 +B9q9KFBmo4Ahh/6+d4wM1rH9kxl0RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF +2cr3wQwCfF1qVu4eAVNVfxfy/uEvG3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgG +QAvkknWitpRK8KVLypEj5WKej6CF8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7 +VwIDAQAB +-----END PUBLIC KEY----- diff --git a/projects/TestApplications/OAuth2/uaa/uaa.yml b/projects/TestApplications/OAuth2/uaa/uaa.yml new file mode 100644 index 0000000000..2534d71b3a --- /dev/null +++ b/projects/TestApplications/OAuth2/uaa/uaa.yml @@ -0,0 +1,171 @@ +logging: + config: /uaa/log4j2.properties + +issuer: + uri: http://local:8080/ + +encryption: + active_key_label: CHANGE-THIS-KEY + encryption_keys: + - label: CHANGE-THIS-KEY + passphrase: CHANGEME + +logout: + redirect: + parameter: + disable: false + whitelist: + http://localhost:15672/* + +login: + serviceProviderKey: | + -----BEGIN RSA PRIVATE KEY----- + MIICXQIBAAKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5 + L39WqS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vA + fpOwznoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQAB + AoGAVOj2Yvuigi6wJD99AO2fgF64sYCm/BKkX3dFEw0vxTPIh58kiRP554Xt5ges + 7ZCqL9QpqrChUikO4kJ+nB8Uq2AvaZHbpCEUmbip06IlgdA440o0r0CPo1mgNxGu + lhiWRN43Lruzfh9qKPhleg2dvyFGQxy5Gk6KW/t8IS4x4r0CQQD/dceBA+Ndj3Xp + ubHfxqNz4GTOxndc/AXAowPGpge2zpgIc7f50t8OHhG6XhsfJ0wyQEEvodDhZPYX + kKBnXNHzAkEAyCA76vAwuxqAd3MObhiebniAU3SnPf2u4fdL1EOm92dyFs1JxyyL + gu/DsjPjx6tRtn4YAalxCzmAMXFSb1qHfwJBAM3qx3z0gGKbUEWtPHcP7BNsrnWK + vw6By7VC8bk/ffpaP2yYspS66Le9fzbFwoDzMVVUO/dELVZyBnhqSRHoXQcCQQCe + A2WL8S5o7Vn19rC0GVgu3ZJlUrwiZEVLQdlrticFPXaFrn3Md82ICww3jmURaKHS + N+l4lnMda79eSp3OMmq9AkA0p79BvYsLshUJJnvbk76pCjR28PK4dV1gSDUEqQMB + qy45ptdwJLqLJCeNoR0JUcDNIRhOCuOPND7pcMtX6hI/ + -----END RSA PRIVATE KEY----- + serviceProviderKeyPassword: password + serviceProviderCertificate: | + -----BEGIN CERTIFICATE----- + MIIDSTCCArKgAwIBAgIBADANBgkqhkiG9w0BAQQFADB8MQswCQYDVQQGEwJhdzEO + MAwGA1UECBMFYXJ1YmExDjAMBgNVBAoTBWFydWJhMQ4wDAYDVQQHEwVhcnViYTEO + MAwGA1UECxMFYXJ1YmExDjAMBgNVBAMTBWFydWJhMR0wGwYJKoZIhvcNAQkBFg5h + cnViYUBhcnViYS5hcjAeFw0xNTExMjAyMjI2MjdaFw0xNjExMTkyMjI2MjdaMHwx + CzAJBgNVBAYTAmF3MQ4wDAYDVQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAM + BgNVBAcTBWFydWJhMQ4wDAYDVQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAb + BgkqhkiG9w0BCQEWDmFydWJhQGFydWJhLmFyMIGfMA0GCSqGSIb3DQEBAQUAA4GN + ADCBiQKBgQDHtC5gUXxBKpEqZTLkNvFwNGnNIkggNOwOQVNbpO0WVHIivig5L39W + qS9u0hnA+O7MCA/KlrAR4bXaeVVhwfUPYBKIpaaTWFQR5cTR1UFZJL/OF9vAfpOw + znoD66DDCnQVpbCjtDYWX+x6imxn8HCYxhMol6ZnTbSsFW6VZjFMjQIDAQABo4Ha + MIHXMB0GA1UdDgQWBBTx0lDzjH/iOBnOSQaSEWQLx1syGDCBpwYDVR0jBIGfMIGc + gBTx0lDzjH/iOBnOSQaSEWQLx1syGKGBgKR+MHwxCzAJBgNVBAYTAmF3MQ4wDAYD + VQQIEwVhcnViYTEOMAwGA1UEChMFYXJ1YmExDjAMBgNVBAcTBWFydWJhMQ4wDAYD + VQQLEwVhcnViYTEOMAwGA1UEAxMFYXJ1YmExHTAbBgkqhkiG9w0BCQEWDmFydWJh + QGFydWJhLmFyggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAYvBJ + 0HOZbbHClXmGUjGs+GS+xC1FO/am2suCSYqNB9dyMXfOWiJ1+TLJk+o/YZt8vuxC + KdcZYgl4l/L6PxJ982SRhc83ZW2dkAZI4M0/Ud3oePe84k8jm3A7EvH5wi5hvCkK + RpuRBwn3Ei+jCRouxTbzKPsuCVB+1sNyxMTXzf0= + -----END CERTIFICATE----- +#The secret that an external login server will use to authenticate to the uaa using the id `login` +LOGIN_SECRET: loginsecret + +jwt: + token: + policy: + # Will override global validity policies for the default zone only. + accessTokenValiditySeconds: 60 + keys: + legacy-token-key: + signingKey: | + -----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA2dP+vRn+Kj+S/oGd49kq6+CKNAduCC1raLfTH7B3qjmZYm45 + yDl+XmgK9CNmHXkho9qvmhdksdzDVsdeDlhKIdcIWadhqDzdtn1hj/22iUwrhH0b + d475hlKcsiZ+oy/sdgGgAzvmmTQmdMqEXqV2B9q9KFBmo4Ahh/6+d4wM1rH9kxl0 + RvMAKLe+daoIHIjok8hCO4cKQQEw/ErBe4SF2cr3wQwCfF1qVu4eAVNVfxfy/uEv + G3Q7x005P3TcK+QcYgJxav3lictSi5dyWLgGQAvkknWitpRK8KVLypEj5WKej6CF + 8nq30utn15FQg0JkHoqzwiCqqeen8GIPteI7VwIDAQABAoIBAFsB5FszYepa11o3 + 4zSPxgv4qyUjuYf3GfoNW0rRGp3nJLtoHAIYa0CcLX9kzsQfmLtxoY46mdppxr8Z + 2qUZpBdRVO7ILNfyXhthdQKI2NuyFDhtYK1p8bx6BXe095HMcvm2ohjXzPdTP4Hq + HrXAYXjUndUbClbjMJ82AnPF8pM70kBq7g733UqkdfrMuv6/d95Jiyw4cC7dGsI3 + Ruz9DGhiAyCBtQ0tUB+6Kqn5DChSB+ccfMJjr6GnCVYmERxEQ5DJCTIX8am8C6KX + mAxUwHMTsEGBU6GzhcUgAwUFEK3I9RptdlRFp7F8E/P0LxmPkFdgaBNUhrdnB7Y4 + 01n1R1kCgYEA/huFJgwVWSBSK/XIouFuQrxZOI9JbBbdmpFT7SBGCdFg26Or9y7j + +N5HE7yuoZ9PkBh17zzosZdsJhGocRYvO0LSq8cXvKXKCwn2fTMM7uJ/oQe68sxG + cF/fC0M/8LvRESWShH920rrERu0s161RuasdOPre0aXu7ZQzkQ68O6MCgYEA23NO + DHKNblBOdFEWsvotLqV8DrIbQ4le7sSgQr56/bdn9GScZk2JU0f+pqzpiGUy9bIt + 6uujvt5ar0IvpIQVdjf3dbp6Fy+Dwhd4yTR4dMdDECest7jL++/21x8Y0ywFhBIK + yEd+QxpOLXP6qaSKTGxL2rnTXRjl8/g629xQPL0CgYEAkNNOh+jLIgjxzGxA9dRV + 62M91qaTyi8eDkJV+wgx4taaxZP7Jt5qwCSvjegz/5m01wOZ88hbNxx+XxQhVJK4 + SKZFO/I07Sfwh2oeOi0maeBdrYGiY09ZtiJuFRU3FBV3irZHU4zyRBh+VY5HyITX + 12JXPWp+JC7WhkG5QiuLzNECgYEA15OBzICLpx6Es4clAVT6JaSzJcyZM9MyyuOl + e2ubbrpJCK/9ZBIvIPzMj/e0wiSH1wzeRrSM+ud7tkcSfk6ytptsIN67KSOoD3b3 + VNCStEU7ABe5eBG1cRzeI52MyYWpNYBzzyNMSacBvWz9hMD6ivCn44pAtGfNHclw + KKNYvxECgYBOamf25md9Jy6rtQsJVEJWw+8sB4lBlKEEadc5qekR7ZQ0hwj8CnTm + WOo856ynI28Sog62iw8F/do/z0B29RuGuxw+prkBkn3lg/VQXEitzqcYvota6osa + 8XSfaPiTyQwWpzbFNZzzemlTsIDiF3UqwkHvWaMYPDf4Ng3cokPPxw== + -----END RSA PRIVATE KEY----- + +scim: + users: + - rabbit_admin|rabbit_admin|scim.read,openid,rabbitmq.read:*/*,rabbitmq.write:*/*,rabbitmq.configure:*/*,rabbitmq.tag:administrator + - rabbitmq_management|rabbitmq_management|scim.read,openid,rabbitmq.read:*/*,rabbitmq.write:*/*,rabbitmq.configure:*/*,rabbitmq.tag:management + - rabbit_monitor|rabbit_monitor|scim.read,openid,rabbitmq.tag:monitoring + groups: + 'rabbitmq.read:*/*': Read all + 'rabbitmq.write:*/*': Write all + 'rabbitmq.configure:*/*': Configure all + 'rabbitmq.tag:management': Management + 'rabbitmq.tag:monitoring': Monitoring + 'rabbitmq.tag:administrator': Administrator + +oauth: + # Always override clients on startup + client: + override: true + + # List of OAuth clients + clients: + admin: + id: admin + secret: adminsecret + authorized-grant-types: client_credentials + scope: none + authorities: uaa.admin,clients.admin,clients.read,clients.write,clients.secret,scim.write,scim.read,uaa.resource + mgt_api_client: + id: mgt_api_client + secret: mgt_api_client + authorized-grant-types: client_credentials + authorities: rabbitmq.tag:monitoring + rabbit_client_code: + id: rabbit_client_code + secret: rabbit_client_code + authorized-grant-types: authorization_code + scope: rabbitmq.*,openid,profile + authorities: uaa.resource,rabbitmq + redirect-uri: http://localhost:15672 + allowpublic: true + mgt_api_client_2: + id: mgt_api_client_2 + secret: mgt_api_client_2 + authorized-grant-types: client_credentials + authorities: api://rabbitmq:management + producer: + id: producer + secret: producer_secret + authorities: rabbitmq.write:*/*,rabbitmq.read:*/*,rabbitmq.configure:*/* + authorized-grant-types: client_credentials + consumer: + id: consumer + secret: consumer_secret + authorities: rabbitmq.read:*/* rabbitmq.configure:*/* rabbitmq.write:*/x-* rabbitmq.write:*/q-* + authorized-grant-types: client_credentials + jms_producer: + id: jms_producer + secret: jms_producer_secret + authorities: rabbitmq.write:%2F/*,rabbitmq.read:%2F/*,rabbitmq.configure:%2F/*,rabbitmq.configure:*/jms.durable.queues,rabbitmq.write:*/jms.durable.queues,rabbitmq.read:*/jms.durable.queues + authorized-grant-types: client_credentials + jms_consumer: + id: jms_consumer + secret: jms_consumer_secret + authorities: rabbitmq.read:*/* rabbitmq.configure:*/* rabbitmq.write:*/x-* rabbitmq.write:*/q-* rabbitmq.write:*/jms.durable.queues + authorized-grant-types: client_credentials + producer_with_roles: + id: producer_with_roles + secret: producer_with_roles_secret + authorities: rabbitmq.*,api://rabbitmq:producer,api://rabbitmq:Administrator + authorized-grant-types: client_credentials + consumer_with_roles: + id: consumer_with_roles + secret: consumer_with_roles_secret + authorities: rabbitmq.* api://rabbitmq:Read.All api://rabbitmq:Configure.All api://rabbitmq:Write.All + authorized-grant-types: client_credentials diff --git a/projects/Unit/APIApproval.cs b/projects/Unit/APIApproval.cs index 14396c7703..a7cf60b4fd 100644 --- a/projects/Unit/APIApproval.cs +++ b/projects/Unit/APIApproval.cs @@ -29,40 +29,37 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- -using System.Reflection; +using System; using System.Threading.Tasks; - using PublicApiGenerator; - using VerifyTests; - using VerifyXunit; - using Xunit; namespace RabbitMQ.Client.Unit { - [UsesVerify] public class APIApproval { - [Fact] - public Task Approve() + private static readonly ApiGeneratorOptions opts = new ApiGeneratorOptions { - string publicApi = typeof(ConnectionFactory).Assembly.GeneratePublicApi(new ApiGeneratorOptions + ExcludeAttributes = new[] { - ExcludeAttributes = new[] - { - "System.Runtime.Versioning.TargetFrameworkAttribute", + "System.Runtime.Versioning.TargetFrameworkAttribute", "System.Reflection.AssemblyMetadataAttribute" - } - }); + } + }; + [Theory] + [InlineData("RabbitMQ.Client", typeof(RabbitMQ.Client.ConnectionFactory))] + [InlineData("RabbitMQ.Client.OAuth2", typeof(RabbitMQ.Client.OAuth2.IOAuth2Client))] + public Task Approve(string dirName, Type apiType) + { + string publicApi = apiType.Assembly.GeneratePublicApi(opts); var settings = new VerifySettings(); + settings.UseDirectory(dirName); settings.DisableDiff(); - return Verifier.Verify(publicApi, settings); } } } - diff --git a/projects/Unit/Fixtures.cs b/projects/Unit/Fixtures.cs index 74d4716902..f3ea9be1a0 100644 --- a/projects/Unit/Fixtures.cs +++ b/projects/Unit/Fixtures.cs @@ -111,16 +111,22 @@ internal AutorecoveringConnection CreateAutorecoveringConnection() return CreateAutorecoveringConnection(RECOVERY_INTERVAL); } + internal AutorecoveringConnection CreateAutorecoveringConnection(ICredentialsProvider credentialsProvider) + { + return CreateAutorecoveringConnection(RECOVERY_INTERVAL, credentialsProvider); + } + internal AutorecoveringConnection CreateAutorecoveringConnection(IList hostnames) { return CreateAutorecoveringConnection(RECOVERY_INTERVAL, hostnames); } - internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan interval) + internal AutorecoveringConnection CreateAutorecoveringConnection(TimeSpan interval, ICredentialsProvider credentialsProvider = null) { var cf = new ConnectionFactory { AutomaticRecoveryEnabled = true, + CredentialsProvider = credentialsProvider, NetworkRecoveryInterval = interval }; return (AutorecoveringConnection)cf.CreateConnection($"{_testDisplayName}:{Guid.NewGuid()}"); @@ -471,6 +477,43 @@ private static bool IsRunningInCI() } } + public sealed class IgnoreOnVersionsEarlierThan : FactAttribute + { + public IgnoreOnVersionsEarlierThan(int major, int minor) + { + if (!CheckMiniumVersion(new Version(major, minor))) + { + Skip = $"Skipped test. It requires RabbitMQ +{major}.{minor}"; + } + } + + private bool CheckMiniumVersion(Version miniumVersion) + { + using (var _conn = new ConnectionFactory().CreateConnection()) + { + System.Collections.Generic.IDictionary properties = _conn.ServerProperties; + + if (properties.TryGetValue("version", out object versionVal)) + { + string versionStr = Encoding.UTF8.GetString((byte[])versionVal); + + int dashIdx = Math.Max(versionStr.IndexOf('-'), versionStr.IndexOf('+')); + if (dashIdx > 0) + { + versionStr = versionStr.Remove(dashIdx); + } + + if (Version.TryParse(versionStr, out Version version)) + { + return version >= miniumVersion; + } + } + + return false; + } + } + } + public class TimingFixture { public static readonly TimeSpan TimingInterval = TimeSpan.FromMilliseconds(300); diff --git a/projects/Unit/Helper/RequestFormMatcher.cs b/projects/Unit/Helper/RequestFormMatcher.cs new file mode 100644 index 0000000000..9691c2495f --- /dev/null +++ b/projects/Unit/Helper/RequestFormMatcher.cs @@ -0,0 +1,63 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; +using System.Collections.Specialized; +using Xunit; + +namespace RabbitMQ.Client.Unit +{ + public class RequestFormMatcher + { + private NameValueCollection _expected = new NameValueCollection(); + + public RequestFormMatcher WithParam(string key, string value) + { + _expected[key] = value; + return this; + } + + public Func Matcher() + { + return (body) => + { + NameValueCollection actual = System.Web.HttpUtility.ParseQueryString(body); + Assert.Equal(actual.Count, _expected.Count); + foreach (string k in actual.Keys) + { + Assert.NotNull(_expected[k]); + Assert.Equal(actual[k], _expected[k]); + } + return true; + }; + } + } +} diff --git a/projects/Unit/RabbitMQ.Client.OAuth2/APIApproval.Approve.verified.txt b/projects/Unit/RabbitMQ.Client.OAuth2/APIApproval.Approve.verified.txt new file mode 100644 index 0000000000..d1a77036a1 --- /dev/null +++ b/projects/Unit/RabbitMQ.Client.OAuth2/APIApproval.Approve.verified.txt @@ -0,0 +1,50 @@ +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Benchmarks, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo(@"Unit, PublicKey=00240000048000009400000006020000002400005253413100040000010001008d20ec856aeeb8c3153a77faa2d80e6e43b5db93224a20cc7ae384f65f142e89730e2ff0fcc5d578bbe96fa98a7196c77329efdee4579b3814c0789e5a39b51df6edd75b602a33ceabdfcf19a3feb832f31d8254168cd7ba5700dfbca301fbf8db614ba41ba18474de0a5f4c2d51c995bc3636c641c8cbe76f45717bfcb943b5")] +namespace RabbitMQ.Client.OAuth2 +{ + public interface IOAuth2Client + { + RabbitMQ.Client.OAuth2.IToken RefreshToken(RabbitMQ.Client.OAuth2.IToken token); + RabbitMQ.Client.OAuth2.IToken RequestToken(); + } + public interface IToken + { + string AccessToken { get; } + System.TimeSpan ExpiresIn { get; } + string RefreshToken { get; } + bool hasExpired { get; } + } + public class JsonToken + { + public JsonToken() { } + public JsonToken(string access_token, string refresh_token, long expires_in) { } + public JsonToken(string access_token, string refresh_token, System.TimeSpan expires_in_span) { } + public string access_token { get; set; } + public long expires_in { get; set; } + public string refresh_token { get; set; } + } + public class OAuth2ClientBuilder + { + public OAuth2ClientBuilder(string clientId, string clientSecret, System.Uri tokenEndpoint) { } + public RabbitMQ.Client.OAuth2.OAuth2ClientBuilder AddRequestParameter(string param, string paramValue) { } + public RabbitMQ.Client.OAuth2.IOAuth2Client Build() { } + public RabbitMQ.Client.OAuth2.OAuth2ClientBuilder SetHttpClientHandler(System.Net.Http.HttpClientHandler handler) { } + public RabbitMQ.Client.OAuth2.OAuth2ClientBuilder SetScope(string scope) { } + } + public class OAuth2ClientCredentialsProvider : RabbitMQ.Client.ICredentialsProvider + { + public OAuth2ClientCredentialsProvider(string name, RabbitMQ.Client.OAuth2.IOAuth2Client oAuth2Client) { } + public string Name { get; } + public string Password { get; } + public string UserName { get; } + public System.TimeSpan? ValidUntil { get; } + public void Refresh() { } + } + public class Token : RabbitMQ.Client.OAuth2.IToken + { + public Token(RabbitMQ.Client.OAuth2.JsonToken json) { } + public string AccessToken { get; } + public System.TimeSpan ExpiresIn { get; } + public string RefreshToken { get; } + } +} \ No newline at end of file diff --git a/projects/Unit/APIApproval.Approve.verified.txt b/projects/Unit/RabbitMQ.Client/APIApproval.Approve.verified.txt similarity index 95% rename from projects/Unit/APIApproval.Approve.verified.txt rename to projects/Unit/RabbitMQ.Client/APIApproval.Approve.verified.txt index b18b6eab38..a02180eca0 100644 --- a/projects/Unit/APIApproval.Approve.verified.txt +++ b/projects/Unit/RabbitMQ.Client/APIApproval.Approve.verified.txt @@ -53,6 +53,15 @@ namespace RabbitMQ.Client public virtual System.Threading.Tasks.Task HandleChannelShutdown(object channel, RabbitMQ.Client.ShutdownEventArgs reason) { } public virtual System.Threading.Tasks.Task OnCancel(params string[] consumerTags) { } } + public class BasicCredentialsProvider : RabbitMQ.Client.ICredentialsProvider + { + public BasicCredentialsProvider(string name, string userName, string password) { } + public string Name { get; } + public string Password { get; } + public string UserName { get; } + public System.TimeSpan? ValidUntil { get; } + public void Refresh() { } + } public sealed class BasicGetResult : System.IDisposable { public BasicGetResult(ulong deliveryTag, bool redelivered, string exchange, string routingKey, uint messageCount, in RabbitMQ.Client.ReadOnlyBasicProperties basicProperties, System.ReadOnlyMemory body) { } @@ -131,6 +140,8 @@ namespace RabbitMQ.Client } public sealed class ConnectionConfig { + public RabbitMQ.Client.ICredentialsProvider CredentialsProvider; + public RabbitMQ.Client.ICredentialsRefresher CredentialsRefresher; public System.Collections.Generic.IList AuthMechanisms { get; } public System.Collections.Generic.IDictionary ClientProperties { get; } public string? ClientProvidedName { get; } @@ -160,6 +171,7 @@ namespace RabbitMQ.Client public const string DefaultVHost = "/"; public static readonly System.Collections.Generic.IList DefaultAuthMechanisms; public static readonly System.TimeSpan DefaultConnectionTimeout; + public static readonly RabbitMQ.Client.ICredentialsRefresher DefaultCredentialsRefresher; public static readonly System.TimeSpan DefaultHeartbeat; public ConnectionFactory() { } public System.Security.Authentication.SslProtocols AmqpUriSslProtocols { get; set; } @@ -169,6 +181,8 @@ namespace RabbitMQ.Client public string ClientProvidedName { get; set; } public int ConsumerDispatchConcurrency { get; set; } public System.TimeSpan ContinuationTimeout { get; set; } + public RabbitMQ.Client.ICredentialsProvider CredentialsProvider { get; set; } + public RabbitMQ.Client.ICredentialsRefresher CredentialsRefresher { get; set; } public bool DispatchConsumersAsync { get; set; } public RabbitMQ.Client.AmqpTcpEndpoint Endpoint { get; set; } public System.Func, RabbitMQ.Client.IEndpointResolver> EndpointResolverFactory { get; set; } @@ -512,6 +526,8 @@ namespace RabbitMQ.Client string ClientProvidedName { get; set; } int ConsumerDispatchConcurrency { get; set; } System.TimeSpan ContinuationTimeout { get; set; } + RabbitMQ.Client.ICredentialsProvider CredentialsProvider { get; set; } + RabbitMQ.Client.ICredentialsRefresher CredentialsRefresher { get; set; } bool DispatchConsumersAsync { get; set; } System.TimeSpan HandshakeContinuationTimeout { get; set; } string Password { get; set; } @@ -529,6 +545,20 @@ namespace RabbitMQ.Client RabbitMQ.Client.IConnection CreateConnection(System.Collections.Generic.IList endpoints, string clientProvidedName); RabbitMQ.Client.IConnection CreateConnection(System.Collections.Generic.IList hostnames, string clientProvidedName); } + public interface ICredentialsProvider + { + string Name { get; } + string Password { get; } + string UserName { get; } + System.TimeSpan? ValidUntil { get; } + void Refresh(); + } + public interface ICredentialsRefresher + { + RabbitMQ.Client.ICredentialsProvider Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshed callback); + bool Unregister(RabbitMQ.Client.ICredentialsProvider provider); + public delegate void NotifyCredentialRefreshed(bool succesfully); + } public interface IEndpointResolver { System.Collections.Generic.IEnumerable All(); @@ -734,6 +764,28 @@ namespace RabbitMQ.Client public string ServerName { get; set; } public System.Security.Authentication.SslProtocols Version { get; set; } } + public class TimerBasedCredentialRefresher : RabbitMQ.Client.ICredentialsRefresher + { + public TimerBasedCredentialRefresher() { } + public RabbitMQ.Client.ICredentialsProvider Register(RabbitMQ.Client.ICredentialsProvider provider, RabbitMQ.Client.ICredentialsRefresher.NotifyCredentialRefreshed callback) { } + public bool Unregister(RabbitMQ.Client.ICredentialsProvider provider) { } + } + [System.Diagnostics.Tracing.EventSource(Name="TimerBasedCredentialRefresher")] + public class TimerBasedCredentialRefresherEventSource : System.Diagnostics.Tracing.EventSource + { + public TimerBasedCredentialRefresherEventSource() { } + public static RabbitMQ.Client.TimerBasedCredentialRefresherEventSource Log { get; } + [System.Diagnostics.Tracing.Event(5)] + public void RefreshedCredentials(string name, bool succesfully) { } + [System.Diagnostics.Tracing.Event(1)] + public void Registered(string name) { } + [System.Diagnostics.Tracing.Event(3)] + public void ScheduledTimer(string name, double interval) { } + [System.Diagnostics.Tracing.Event(4)] + public void TriggeredTimer(string name) { } + [System.Diagnostics.Tracing.Event(2)] + public void Unregistered(string name) { } + } public class TopologyRecoveryExceptionHandler { public TopologyRecoveryExceptionHandler() { } diff --git a/projects/Unit/RabbitMQCtl.cs b/projects/Unit/RabbitMQCtl.cs index 057f917c28..7a12e97f51 100644 --- a/projects/Unit/RabbitMQCtl.cs +++ b/projects/Unit/RabbitMQCtl.cs @@ -236,5 +236,24 @@ public static void AwaitRabbitMQ() { ExecRabbitMQCtl("await_startup"); } + + public static void AddUser(string username, string password) + { + ExecRabbitMQCtl($"add_user {username} {password}"); + } + public static void ChangePassword(string username, string password) + { + ExecRabbitMQCtl($"change_password {username} {password}"); + } + + public static void SetPermissions(string username, string conf, string write, string read) + { + ExecRabbitMQCtl($"set_permissions {username} \"{conf}\" \"{write}\" \"${read}\" "); + } + + public static void DeleteUser(string username) + { + ExecRabbitMQCtl($"delete_user {username}"); + } } } diff --git a/projects/Unit/TestOAuth2Client.cs b/projects/Unit/TestOAuth2Client.cs new file mode 100644 index 0000000000..88a7fba645 --- /dev/null +++ b/projects/Unit/TestOAuth2Client.cs @@ -0,0 +1,168 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; +using System.Net.Http; +using System.Text.Json; +using RabbitMQ.Client.OAuth2; +using WireMock.RequestBuilders; +using WireMock.ResponseBuilders; +using WireMock.Server; +using Xunit; + +namespace RabbitMQ.Client.Unit +{ + public class TestOAuth2Client + { + protected string _client_id = "producer"; + protected string _client_secret = "kbOFBXI9tANgKUq8vXHLhT6YhbivgXxn"; + protected WireMockServer _oauthServer; + + protected IOAuth2Client _client; + + public TestOAuth2Client() + { + _oauthServer = WireMockServer.Start(); + + _client = new OAuth2ClientBuilder(_client_id, _client_secret, new System.Uri(_oauthServer.Url + "/token")).Build(); + } + + private void expectTokenRequest(RequestFormMatcher expectedRequestBody, JsonToken expectedResponse) + { + _oauthServer + .Given( + Request.Create() + .WithPath("/token") + .WithBody(expectedRequestBody.Matcher()) + .UsingPost() + ) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithHeader("Content-Type", "application/json;charset=UTF-8") + .WithBody(JsonSerializer.Serialize(expectedResponse)) + ); + } + + [Fact] + public void TestRequestToken() + { + JsonToken expectedJsonToken = new JsonToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(10)); + expectTokenRequest(new RequestFormMatcher() + .WithParam("client_id", _client_id) + .WithParam("client_secret", _client_secret) + .WithParam("grant_type", "client_credentials"), + expectedJsonToken); + + IToken token = _client.RequestToken(); + Assert.NotNull(token); + Assert.Equal(token.AccessToken, expectedJsonToken.access_token); + Assert.Equal(token.RefreshToken, expectedJsonToken.refresh_token); + Assert.Equal(token.ExpiresIn, TimeSpan.FromSeconds(expectedJsonToken.expires_in)); + } + + private void expectTokenRefresh(JsonToken expectedResponse) + { + _oauthServer + .Given( + Request.Create() + .WithPath("/token") + .WithParam("client_id", _client_id) + .WithParam("client_secret", _client_secret) + .WithParam("grant_type", "refresh_token") + .WithParam("refresh_token", expectedResponse.refresh_token) + .WithHeader("content_type", "application/x-www-form-urlencoded") + .UsingPost() + ) + .RespondWith( + Response.Create() + .WithStatusCode(200) + .WithHeader("Content-Type", "application/json;charset=UTF-8") + .WithBody(JsonSerializer.Serialize(expectedResponse)) + ); + } + + [Fact] + public void TestRefreshToken() + { + JsonToken expectedJsonToken = new JsonToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(10)); + expectTokenRequest(new RequestFormMatcher() + .WithParam("client_id", _client_id) + .WithParam("client_secret", _client_secret) + .WithParam("grant_type", "client_credentials"), + expectedJsonToken); + + IToken token = _client.RequestToken(); + _oauthServer.Reset(); + + expectedJsonToken = new JsonToken("the_access_token2", "the_refresh_token", TimeSpan.FromSeconds(20)); + expectTokenRequest(new RequestFormMatcher() + .WithParam("client_id", _client_id) + .WithParam("client_secret", _client_secret) + .WithParam("grant_type", "refresh_token") + .WithParam("refresh_token", "the_refresh_token"), + expectedJsonToken); + + IToken refreshedToken = _client.RefreshToken(token); + Assert.False(refreshedToken == token); + Assert.NotNull(refreshedToken); + Assert.Equal(refreshedToken.AccessToken, expectedJsonToken.access_token); + Assert.Equal(refreshedToken.RefreshToken, expectedJsonToken.refresh_token); + Assert.Equal(refreshedToken.ExpiresIn, TimeSpan.FromSeconds(expectedJsonToken.expires_in)); + } + + [Fact] + public void TestInvalidCredentials() + { + _oauthServer + .Given( + Request.Create() + .WithPath("/token") + .WithBody(new RequestFormMatcher() + .WithParam("client_id", _client_id) + .WithParam("client_secret", _client_secret) + .WithParam("grant_type", "client_credentials").Matcher()) + .UsingPost() + ) + .RespondWith( + Response.Create() + .WithStatusCode(401) + ); + + try + { + IToken token = _client.RequestToken(); + Assert.Fail("Should have thrown Exception"); + } + catch (HttpRequestException) { } + } + } +} diff --git a/projects/Unit/TestOAuth2ClientCredentialsProvider.cs b/projects/Unit/TestOAuth2ClientCredentialsProvider.cs new file mode 100644 index 0000000000..c9149c96b7 --- /dev/null +++ b/projects/Unit/TestOAuth2ClientCredentialsProvider.cs @@ -0,0 +1,123 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; + +using Moq; +using RabbitMQ.Client.OAuth2; +using Xunit; + +namespace RabbitMQ.Client.Unit +{ + public class TestOAuth2CredentialsProvider + { + protected OAuth2ClientCredentialsProvider _provider; + protected Mock _oAuth2Client; + + public TestOAuth2CredentialsProvider() + { + _oAuth2Client = new Mock(); + _provider = new OAuth2ClientCredentialsProvider("aName", _oAuth2Client.Object); + } + + [Fact] + public void shouldHaveAName() + { + Assert.Equal("aName", _provider.Name); + } + + [Fact] + public void ShouldRequestTokenWhenAskToRefresh() + { + _oAuth2Client.Setup(p => p.RequestToken()).Returns(newToken("the_access_token", TimeSpan.FromSeconds(60))); + _provider.Refresh(); + Assert.Equal("the_access_token", _provider.Password); + } + + [Fact] + public void ShouldRequestTokenWhenGettingPasswordOrValidUntilForFirstTimeAccess() + { + IToken firstToken = newToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(1)); + _oAuth2Client.Setup(p => p.RequestToken()).Returns(firstToken); + Assert.Equal(firstToken.AccessToken, _provider.Password); + Assert.Equal(firstToken.ExpiresIn, _provider.ValidUntil.Value); + } + + [Fact] + public void ShouldRefreshTokenUsingRefreshTokenWhenAvailable() + { + IToken firstToken = newToken("the_access_token", "the_refresh_token", TimeSpan.FromSeconds(1)); + IToken refreshedToken = newToken("the_access_token2", "the_refresh_token_2", TimeSpan.FromSeconds(60)); + _oAuth2Client.Setup(p => p.RequestToken()).Returns(firstToken); + _provider.Refresh(); + Assert.Equal(firstToken.AccessToken, _provider.Password); + Assert.Equal(firstToken.ExpiresIn, _provider.ValidUntil.Value); + _oAuth2Client.Reset(); + System.Threading.Thread.Sleep(1000); + + _oAuth2Client.Setup(p => p.RefreshToken(firstToken)).Returns(refreshedToken); + _provider.Refresh(); + Assert.Equal(refreshedToken.AccessToken, _provider.Password); + Assert.Equal(refreshedToken.ExpiresIn, _provider.ValidUntil.Value); + + } + + [Fact] + public void ShouldRequestTokenWhenRefreshTokenNotAvailable() + { + IToken firstToken = newToken("the_access_token", null, TimeSpan.FromSeconds(1)); + IToken refreshedToken = newToken("the_access_token2", null, TimeSpan.FromSeconds(1)); + _oAuth2Client.SetupSequence(p => p.RequestToken()) + .Returns(firstToken) + .Returns(refreshedToken); + _provider.Refresh(); + Assert.Equal(firstToken.AccessToken, _provider.Password); + Assert.Equal(firstToken.ExpiresIn, _provider.ValidUntil.Value); + + _provider.Refresh(); + Assert.Equal(refreshedToken.AccessToken, _provider.Password); + Assert.Equal(refreshedToken.ExpiresIn, _provider.ValidUntil.Value); + Mock.Verify(_oAuth2Client); + } + + private static Token newToken(string access_token, TimeSpan expiresIn) + { + var token = new JsonToken(access_token, string.Empty, expiresIn); + return new Token(token); + } + + private static Token newToken(string access_token, string refresh_token, TimeSpan expiresIn) + { + JsonToken token = new JsonToken(access_token, refresh_token, expiresIn); + return new Token(token); + } + } +} diff --git a/projects/Unit/TestTimerBasedCredentialRefresher.cs b/projects/Unit/TestTimerBasedCredentialRefresher.cs new file mode 100644 index 0000000000..ce5047ba37 --- /dev/null +++ b/projects/Unit/TestTimerBasedCredentialRefresher.cs @@ -0,0 +1,97 @@ +// This source code is dual-licensed under the Apache License, version +// 2.0, and the Mozilla Public License, version 2.0. +// +// The APL v2.0: +// +//--------------------------------------------------------------------------- +// Copyright (c) 2007-2020 VMware, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://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. +//--------------------------------------------------------------------------- +// +// The MPL v2.0: +// +//--------------------------------------------------------------------------- +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at https://mozilla.org/MPL/2.0/. +// +// Copyright (c) 2007-2020 VMware, Inc. All rights reserved. +//--------------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Moq; +using Xunit; + +namespace RabbitMQ.Client.Unit +{ + public class TestTimerBasedCredentialsRefresher + { + protected TimerBasedCredentialRefresher _refresher; + protected Mock _credentialsProvider; + protected Mock _callback = new Mock(); + + public TestTimerBasedCredentialsRefresher() + { + _refresher = new TimerBasedCredentialRefresher(); + _credentialsProvider = new Mock(); + } + + [Fact] + public void TestRegister() + { + _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.FromSeconds(1)); + Assert.True(_credentialsProvider.Object == _refresher.Register(_credentialsProvider.Object, _callback.Object)); + Assert.True(_refresher.Unregister(_credentialsProvider.Object)); + } + + [Fact] + public void TestDoNotRegisterWhenHasNoExpiry() + { + + _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.Zero); + _refresher.Register(_credentialsProvider.Object, _callback.Object); + Assert.False(_refresher.Unregister(_credentialsProvider.Object)); + _credentialsProvider.Verify(); + } + + [Fact] + public async void TestRefreshToken() + { + _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.FromSeconds(1)); + _credentialsProvider.Setup(p => p.Password).Returns("the-token").Verifiable(); + _callback.Setup(p => p.Invoke(true)); + _refresher.Register(_credentialsProvider.Object, _callback.Object); + await Task.Delay(1 * 1000); + + _credentialsProvider.Verify(); + _callback.Verify(); + } + + [Fact] + public async void TestRefreshTokenFailed() + { + _credentialsProvider.Setup(p => p.ValidUntil).Returns(TimeSpan.FromSeconds(1)); + _credentialsProvider.SetupSequence(p => p.Password) + .Returns("the-token") + .Throws(new Exception()); + _callback.Setup(p => p.Invoke(false)); + _refresher.Register(_credentialsProvider.Object, _callback.Object); + await Task.Delay(1 * 1000); + + _credentialsProvider.Verify(); + _callback.Verify(); + } + } +} diff --git a/projects/Unit/TestUpdateSecret.cs b/projects/Unit/TestUpdateSecret.cs index 329e50446d..b81ef5ce15 100644 --- a/projects/Unit/TestUpdateSecret.cs +++ b/projects/Unit/TestUpdateSecret.cs @@ -29,56 +29,20 @@ // Copyright (c) 2007-2020 VMware, Inc. All rights reserved. //--------------------------------------------------------------------------- -using System; -using System.Text; -using RabbitMQ.Client.Framing.Impl; -using Xunit; using Xunit.Abstractions; namespace RabbitMQ.Client.Unit { - public class TestUpdateSecret : IntegrationFixture { public TestUpdateSecret(ITestOutputHelper output) : base(output) { } - [Fact] + [IgnoreOnVersionsEarlierThan(3, 8)] public void TestUpdatingConnectionSecret() { - if (!RabbitMQ380OrHigher()) - { - Console.WriteLine("Not connected to RabbitMQ 3.8 or higher, skipping test"); - return; - } - _conn.UpdateSecret("new-secret", "Test Case"); - - Assert.Equal("new-secret", ((AutorecoveringConnection)_conn).Password); - } - - private bool RabbitMQ380OrHigher() - { - System.Collections.Generic.IDictionary properties = _conn.ServerProperties; - - if (properties.TryGetValue("version", out object versionVal)) - { - string versionStr = Encoding.UTF8.GetString((byte[])versionVal); - - int dashIdx = versionStr.IndexOf('-'); - if (dashIdx > 0) - { - versionStr = versionStr.Remove(dashIdx); - } - - if (Version.TryParse(versionStr, out Version version)) - { - return version >= new Version(3, 8); - } - } - - return false; } } } diff --git a/projects/Unit/Unit.csproj b/projects/Unit/Unit.csproj index d64fa1f8d8..949d0ab78d 100644 --- a/projects/Unit/Unit.csproj +++ b/projects/Unit/Unit.csproj @@ -17,6 +17,7 @@ + @@ -27,12 +28,13 @@ runtime; build; native; contentfiles; analyzers; buildtransitive all + - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + + + +