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 0000000000..092bfef15c Binary files /dev/null and b/projects/RabbitMQ.Client.OAuth2/icon.png differ 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..ebcd438924 --- /dev/null +++ b/projects/RabbitMQ.Client/client/api/ICredentialsRefresher.cs @@ -0,0 +1,141 @@ +// 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..684dfe045a 100644 --- a/projects/Unit/Fixtures.cs +++ b/projects/Unit/Fixtures.cs @@ -110,17 +110,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()}"); @@ -470,7 +475,42 @@ private static bool IsRunningInCI() return true; } } + 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..4aebc9e393 --- /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(token.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 - + + + +