Skip to content

Commit 8d74734

Browse files
committed
Merge branch 'dev' into v1.19.1
2 parents 3470d1f + 0800589 commit 8d74734

File tree

38 files changed

+489
-243
lines changed

38 files changed

+489
-243
lines changed

AzureSignalR.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatSample.Net60", "samples
8282
EndProject
8383
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ChatSample.Net70", "samples\ChatSample\ChatSample.Net70\ChatSample.Net70.csproj", "{49634EE4-A0F4-4672-A8B3-B994CF81C9AB}"
8484
EndProject
85+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ManagementPublisher", "samples\ChatSample\ChatSample.ManagementPublisher\ManagementPublisher.csproj", "{0F32E624-7AC8-4CA7-8ED9-E1A877442020}"
86+
EndProject
8587
Global
8688
GlobalSection(SolutionConfigurationPlatforms) = preSolution
8789
Debug|Any CPU = Debug|Any CPU
@@ -196,6 +198,10 @@ Global
196198
{49634EE4-A0F4-4672-A8B3-B994CF81C9AB}.Debug|Any CPU.Build.0 = Debug|Any CPU
197199
{49634EE4-A0F4-4672-A8B3-B994CF81C9AB}.Release|Any CPU.ActiveCfg = Release|Any CPU
198200
{49634EE4-A0F4-4672-A8B3-B994CF81C9AB}.Release|Any CPU.Build.0 = Release|Any CPU
201+
{0F32E624-7AC8-4CA7-8ED9-E1A877442020}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
202+
{0F32E624-7AC8-4CA7-8ED9-E1A877442020}.Debug|Any CPU.Build.0 = Debug|Any CPU
203+
{0F32E624-7AC8-4CA7-8ED9-E1A877442020}.Release|Any CPU.ActiveCfg = Release|Any CPU
204+
{0F32E624-7AC8-4CA7-8ED9-E1A877442020}.Release|Any CPU.Build.0 = Release|Any CPU
199205
EndGlobalSection
200206
GlobalSection(SolutionProperties) = preSolution
201207
HideSolutionNode = FALSE
@@ -230,6 +236,7 @@ Global
230236
{82C1FF3D-EC6C-4B21-B6A4-E69E8D75D0D0} = {2429FBD8-1FCE-4C42-AA28-DF32F7249E77}
231237
{594EC59A-7305-4A36-8BE6-4A928FBFD71B} = {C965ED06-6A17-4329-B3C6-811830F4F4ED}
232238
{49634EE4-A0F4-4672-A8B3-B994CF81C9AB} = {C965ED06-6A17-4329-B3C6-811830F4F4ED}
239+
{0F32E624-7AC8-4CA7-8ED9-E1A877442020} = {C965ED06-6A17-4329-B3C6-811830F4F4ED}
233240
EndGlobalSection
234241
GlobalSection(ExtensibilityGlobals) = postSolution
235242
SolutionGuid = {7945A4E4-ACDB-4F6E-95CA-6AC6E7C2CD59}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="..\..\..\src\Microsoft.Azure.SignalR.Management\Microsoft.Azure.SignalR.Management.csproj" />
11+
</ItemGroup>
12+
13+
</Project>
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Microsoft.Azure.SignalR.Management;
5+
6+
// A simple library file to test cross project reference issue: https://github.com/Azure/azure-signalr/issues/1720
7+
namespace ManagementPublisher
8+
{
9+
internal class MessagePublisher
10+
{
11+
private const string Target = "Target";
12+
private const string HubName = "Chat";
13+
private ServiceHubContext? _hubContext;
14+
15+
public async Task InitAsync(string connectionString, ServiceTransportType transportType = ServiceTransportType.Transient)
16+
{
17+
var serviceManager = new ServiceManagerBuilder().WithOptions(option =>
18+
{
19+
option.ConnectionString = connectionString;
20+
option.ServiceTransportType = transportType;
21+
})
22+
// Uncomment the following line to get more logs
23+
//.WithLoggerFactory(LoggerFactory.Create(builder => builder.AddConsole()))
24+
.BuildServiceManager();
25+
26+
_hubContext = await serviceManager.CreateHubContextAsync(HubName, default);
27+
}
28+
29+
public Task SendMessages(string command, string? receiver, string message)
30+
{
31+
if (_hubContext == null)
32+
{
33+
throw new ArgumentNullException(nameof(_hubContext));
34+
}
35+
switch (command)
36+
{
37+
case "broadcast":
38+
return _hubContext.Clients.All.SendCoreAsync(Target, new[] { message });
39+
case "user":
40+
var userId = receiver ?? throw new ArgumentNullException(nameof(receiver));
41+
return _hubContext.Clients.User(userId).SendCoreAsync(Target, new[] { message });
42+
case "group":
43+
var groupName = receiver ?? throw new ArgumentNullException(nameof(receiver));
44+
return _hubContext.Clients.Group(groupName).SendCoreAsync(Target, new[] { message });
45+
default:
46+
Console.WriteLine($"Can't recognize command {command}");
47+
return Task.CompletedTask;
48+
}
49+
}
50+
51+
public Task CloseConnection(string connectionId, string reason)
52+
{
53+
if (_hubContext == null)
54+
{
55+
throw new ArgumentNullException(nameof(_hubContext));
56+
}
57+
return _hubContext.ClientManager.CloseConnectionAsync(connectionId, reason);
58+
}
59+
60+
public Task<bool> CheckExist(string type, string id)
61+
{
62+
if (_hubContext == null)
63+
{
64+
throw new ArgumentNullException(nameof(_hubContext));
65+
}
66+
return type switch
67+
{
68+
"connection" => _hubContext.ClientManager.ConnectionExistsAsync(id),
69+
"user" => _hubContext.ClientManager.UserExistsAsync(id),
70+
"group" => _hubContext.ClientManager.UserExistsAsync(id),
71+
_ => throw new NotSupportedException(),
72+
};
73+
}
74+
75+
public async Task DisposeAsync()
76+
{
77+
if (_hubContext != null)
78+
{
79+
await _hubContext.DisposeAsync();
80+
}
81+
}
82+
}
83+
}

src/Microsoft.Azure.SignalR.AspNet/EndpointProvider/ServiceEndpointProvider.cs

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,26 @@ internal class ServiceEndpointProvider : IServiceEndpointProvider
1818
"or explicitly pass one using IAppBuilder.RunAzureSignalR(connectionString) in Startup.ConfigureServices.";
1919

2020
private const string ClientPath = "aspnetclient";
21+
2122
private const string ServerPath = "aspnetserver";
2223

2324
private readonly string _audienceBaseUrl;
25+
2426
private readonly string _clientEndpoint;
27+
2528
private readonly string _serverEndpoint;
2629

2730
private readonly AccessKey _accessKey;
31+
2832
private readonly string _appName;
33+
2934
private readonly TimeSpan _accessTokenLifetime;
35+
3036
private readonly AccessTokenAlgorithm _algorithm;
3137

3238
public IWebProxy Proxy { get; }
3339

34-
public ServiceEndpointProvider(
35-
ServiceEndpoint endpoint,
36-
ServiceOptions options)
40+
public ServiceEndpointProvider(ServiceEndpoint endpoint, ServiceOptions options)
3741
{
3842
_accessTokenLifetime = options.AccessTokenLifetime;
3943

@@ -48,39 +52,13 @@ public ServiceEndpointProvider(
4852
Proxy = options.Proxy;
4953
}
5054

51-
private string GetPrefixedHubName(string applicationName, string hubName)
52-
{
53-
return string.IsNullOrEmpty(applicationName) ? hubName.ToLower() : $"{applicationName.ToLower()}_{hubName.ToLower()}";
54-
}
55-
5655
public Task<string> GenerateClientAccessTokenAsync(string hubName = null, IEnumerable<Claim> claims = null, TimeSpan? lifetime = null)
5756
{
5857
var audience = $"{_audienceBaseUrl}{ClientPath}";
5958

6059
return _accessKey.GenerateAccessTokenAsync(audience, claims, lifetime ?? _accessTokenLifetime, _algorithm);
6160
}
6261

63-
public Task<string> GenerateServerAccessTokenAsync(string hubName, string userId, TimeSpan? lifetime = null)
64-
{
65-
if (_accessKey is AadAccessKey key)
66-
{
67-
return key.GenerateAadTokenAsync();
68-
}
69-
70-
IEnumerable<Claim> claims = null;
71-
if (userId != null)
72-
{
73-
claims = new[]
74-
{
75-
new Claim(ClaimTypes.NameIdentifier, userId)
76-
};
77-
}
78-
79-
var audience = $"{_audienceBaseUrl}{ServerPath}/?hub={GetPrefixedHubName(_appName, hubName)}";
80-
81-
return _accessKey.GenerateAccessTokenAsync(audience, claims, lifetime ?? _accessTokenLifetime, _algorithm);
82-
}
83-
8462
public string GetClientEndpoint(string hubName = null, string originalPath = null, string queryString = null)
8563
{
8664
var queryBuilder = new StringBuilder();
@@ -114,5 +92,28 @@ public string GetServerEndpoint(string hubName)
11492
{
11593
return $"{_serverEndpoint}{ServerPath}/?hub={GetPrefixedHubName(_appName, hubName)}";
11694
}
95+
96+
public IAccessTokenProvider GetServerAccessTokenProvider(string hubName, string serverId)
97+
{
98+
if (_accessKey is AadAccessKey aadAccessKey)
99+
{
100+
return new AadTokenProvider(aadAccessKey);
101+
}
102+
else if (_accessKey is not null)
103+
{
104+
var audience = $"{_audienceBaseUrl}{ServerPath}/?hub={GetPrefixedHubName(_appName, hubName)}";
105+
var claims = serverId != null ? new[] { new Claim(ClaimTypes.NameIdentifier, serverId) } : null;
106+
return new LocalTokenProvider(_accessKey, audience, claims, _algorithm, _accessTokenLifetime);
107+
}
108+
else
109+
{
110+
throw new ArgumentNullException(nameof(AccessKey));
111+
}
112+
}
113+
114+
private string GetPrefixedHubName(string applicationName, string hubName)
115+
{
116+
return string.IsNullOrEmpty(applicationName) ? hubName.ToLower() : $"{applicationName.ToLower()}_{hubName.ToLower()}";
117+
}
117118
}
118119
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Threading.Tasks;
6+
7+
namespace Microsoft.Azure.SignalR
8+
{
9+
internal class AadTokenProvider : IAccessTokenProvider
10+
{
11+
private readonly AadAccessKey _accessKey;
12+
13+
public AadTokenProvider(AadAccessKey accessKey)
14+
{
15+
_accessKey = accessKey ?? throw new ArgumentNullException(nameof(accessKey));
16+
}
17+
18+
public Task<string> ProvideAsync() => _accessKey.GenerateAadTokenAsync();
19+
}
20+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Security.Claims;
7+
using System.Threading.Tasks;
8+
9+
namespace Microsoft.Azure.SignalR
10+
{
11+
internal class LocalTokenProvider : IAccessTokenProvider
12+
{
13+
private readonly AccessKey _accessKey;
14+
15+
private readonly AccessTokenAlgorithm _algorithm;
16+
17+
private readonly string _audience;
18+
19+
private readonly TimeSpan _tokenLifetime;
20+
21+
private readonly IEnumerable<Claim> _claims;
22+
23+
public LocalTokenProvider(
24+
AccessKey accessKey,
25+
string audience,
26+
IEnumerable<Claim> claims,
27+
AccessTokenAlgorithm algorithm = AccessTokenAlgorithm.HS256,
28+
TimeSpan? tokenLifetime = null)
29+
{
30+
_accessKey = accessKey ?? throw new ArgumentNullException(nameof(accessKey));
31+
_algorithm = algorithm;
32+
_audience = audience;
33+
_claims = claims;
34+
_tokenLifetime = tokenLifetime ?? Constants.Periods.DefaultAccessTokenLifetime;
35+
}
36+
37+
public Task<string> ProvideAsync() => _accessKey.GenerateAccessTokenAsync(_audience, _claims, _tokenLifetime, _algorithm);
38+
}
39+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Microsoft. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using System.Threading.Tasks;
5+
6+
namespace Microsoft.Azure.SignalR
7+
{
8+
internal interface IAccessTokenProvider
9+
{
10+
Task<string> ProvideAsync();
11+
}
12+
}

src/Microsoft.Azure.SignalR.Common/Interfaces/IServiceEndpointProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal interface IServiceEndpointProvider
1515

1616
string GetClientEndpoint(string hubName, string originalPath, string queryString);
1717

18-
Task<string> GenerateServerAccessTokenAsync(string hubName, string userId, TimeSpan? lifetime = null);
18+
IAccessTokenProvider GetServerAccessTokenProvider(string hubName, string serverId);
1919

2020
string GetServerEndpoint(string hubName);
2121

src/Microsoft.Azure.SignalR.Common/RestClients/SignalRServiceRestClient.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ public partial class SignalRServiceRestClient
1515
{
1616
private readonly string _userAgent;
1717

18-
public SignalRServiceRestClient(string userAgent, ServiceClientCredentials credentials, HttpClient httpClient, bool disposeHttpClient) : this(credentials, httpClient, disposeHttpClient)
18+
public SignalRServiceRestClient(string userAgent,
19+
ServiceClientCredentials credentials,
20+
HttpClient httpClient,
21+
bool disposeHttpClient) : this(credentials, httpClient, disposeHttpClient)
1922
{
2023
if (string.IsNullOrWhiteSpace(userAgent))
2124
{

src/Microsoft.Azure.SignalR.Common/ServiceConnections/ConnectionFactory.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ namespace Microsoft.Azure.SignalR
1414
internal class ConnectionFactory : IConnectionFactory
1515
{
1616
private readonly ILoggerFactory _loggerFactory;
17+
1718
private readonly string _serverId;
1819

1920
public ConnectionFactory(IServerNameProvider nameProvider, ILoggerFactory loggerFactory)
@@ -31,7 +32,9 @@ public async Task<ConnectionContext> ConnectAsync(HubServiceEndpoint hubServiceE
3132
{
3233
var provider = hubServiceEndpoint.Provider;
3334
var hubName = hubServiceEndpoint.Hub;
34-
Task<string> accessTokenGenerater() => provider.GenerateServerAccessTokenAsync(hubName, _serverId);
35+
36+
var accessTokenProvider = provider.GetServerAccessTokenProvider(hubName, _serverId);
37+
3538
var url = GetServiceUrl(provider, hubName, connectionId, target);
3639

3740
headers ??= new Dictionary<string, string>();
@@ -45,7 +48,7 @@ public async Task<ConnectionContext> ConnectAsync(HubServiceEndpoint hubServiceE
4548
Headers = headers,
4649
Proxy = provider.Proxy,
4750
};
48-
var connection = new WebSocketConnectionContext(connectionOptions, _loggerFactory, accessTokenGenerater);
51+
var connection = new WebSocketConnectionContext(connectionOptions, _loggerFactory, accessTokenProvider);
4952
try
5053
{
5154
await connection.StartAsync(url, cancellationToken);
@@ -91,6 +94,7 @@ private Uri GetServiceUrl(IServiceEndpointProvider provider, string hubName, str
9194
private sealed class GracefulLoggerFactory : ILoggerFactory
9295
{
9396
private readonly ILoggerFactory _inner;
97+
9498
public GracefulLoggerFactory(ILoggerFactory inner)
9599
{
96100
_inner = inner;
@@ -115,6 +119,7 @@ public void AddProvider(ILoggerProvider provider)
115119
private sealed class GracefulLogger : ILogger
116120
{
117121
private readonly ILogger _inner;
122+
118123
public GracefulLogger(ILogger inner)
119124
{
120125
_inner = inner;

0 commit comments

Comments
 (0)