Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions api/Microsoft.Azure.SignalR.Protocols.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,12 @@ public abstract partial class ExtensibleServiceMessage : Microsoft.Azure.SignalR
{
protected ExtensibleServiceMessage() { }
}
public partial class GetConnectionClaimsMessage : Microsoft.Azure.SignalR.Protocol.ExtensibleServiceMessage, Microsoft.Azure.SignalR.Protocol.IAckableMessage
{
public GetConnectionClaimsMessage(string connectionToken, int ackId) { }
public int AckId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public string ConnectionToken { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
public partial class GroupBroadcastDataMessage : Microsoft.Azure.SignalR.Protocol.MulticastDataMessage, Microsoft.Azure.SignalR.Protocol.IPartitionableMessage
{
public GroupBroadcastDataMessage(string groupName, System.Collections.Generic.IDictionary<string, System.ReadOnlyMemory<byte>> payloads, ulong? tracingId = default(ulong?)) : base (default(System.Collections.Generic.IDictionary<string, System.ReadOnlyMemory<byte>>), default(ulong?)) { }
Expand Down Expand Up @@ -489,6 +495,7 @@ public static partial class ServiceProtocolConstants
public const int ConnectionFlowControlMessageType = 39;
public const int ConnectionReconnectMessageType = 38;
public const int ErrorCompletionMessageType = 36;
public const int GetConnectionClaimsMessageType = 42;
public const int GroupBroadcastDataMessageType = 13;
public const int GroupMemberQueryMessageType = 40;
public const int HandshakeRequestType = 1;
Expand All @@ -506,12 +513,19 @@ public static partial class ServiceProtocolConstants
public const int ServiceErrorMessageType = 15;
public const int ServiceEventMessageType = 22;
public const int ServiceMappingMessageType = 37;
public const int UpdateConnectionClaimsMessageType = 43;
public const int UserDataMessageType = 8;
public const int UserJoinGroupMessageType = 16;
public const int UserJoinGroupWithAckMessageType = 26;
public const int UserLeaveGroupMessageType = 17;
public const int UserLeaveGroupWithAckMessageType = 27;
}
public partial class UpdateConnectionClaimsMessage : Microsoft.Azure.SignalR.Protocol.ExtensibleServiceMessage
{
public UpdateConnectionClaimsMessage(string connectionId, System.Security.Claims.Claim[]? claims) { }
public System.Security.Claims.Claim[]? Claims { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public string ConnectionId { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
public partial class UserDataMessage : Microsoft.Azure.SignalR.Protocol.MulticastDataMessage, Microsoft.Azure.SignalR.Protocol.IPartitionableMessage
{
public UserDataMessage(string userId, System.Collections.Generic.IDictionary<string, System.ReadOnlyMemory<byte>> payloads, ulong? tracingId = default(ulong?)) : base (default(System.Collections.Generic.IDictionary<string, System.ReadOnlyMemory<byte>>), default(ulong?)) { }
Expand Down
24 changes: 24 additions & 0 deletions specs/ServiceProtocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -657,3 +657,27 @@ MessagePack uses different formats to encode values. Refer to the [MessagePack F
- ExtensionMembers - A MessagePack Map indicates the extensible members.

#### Example: TODO

### GetConnectionClaims Message
`GetConnectionClaims` messages have the following structure:
```
[42, ConnectionToken, AckId, ExtensionMembers]
```
- 42 - Message Type, indicating this is a `GetConnectionClaims` message.
- ConnectionToken - A `String` indicating the connection token of the live client connection whose current user claims are being fetched.
- AckId - An `Int32` encoding Id number to identify the corresponding ack message.
- ExtensionMembers - A MessagePack Map indicates the extensible members.

#### Example: TODO

### UpdateConnectionClaims Message
`UpdateConnectionClaims` messages have the following structure:
```
[43, ConnectionId, Claims, ExtensionMembers]
```
- 43 - Message Type, indicating this is an `UpdateConnectionClaims` message.
- ConnectionId - A `String` indicating the connection id of the live client connection whose user claims are being updated on the owning server.
- Claims - A MessagePack Map of `String` to `String` indicating the refreshed user claims to apply.
- ExtensionMembers - A MessagePack Map indicates the extensible members.

#### Example: TODO
54 changes: 54 additions & 0 deletions src/Microsoft.Azure.SignalR.Protocols/ServiceMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,60 @@ public RefreshAuthMessage(string connectionToken, System.Security.Claims.Claim[]
}
}

/// <summary>
/// A read-only message to fetch the current user claims of an existing client connection.
/// </summary>
public class GetConnectionClaimsMessage : ExtensibleServiceMessage, IAckableMessage
{
/// <summary>
/// Gets or sets the connection token that identifies the live client connection whose claims are being fetched.
/// </summary>
public string ConnectionToken { get; set; }

/// <summary>
/// Gets or sets the protocol correlation id used to acknowledge this read operation.
/// </summary>
public int AckId { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="GetConnectionClaimsMessage"/> class.
/// </summary>
/// <param name="connectionToken">The connection token that identifies the live client connection.</param>
/// <param name="ackId">The protocol correlation id used to acknowledge this read operation.</param>
public GetConnectionClaimsMessage(string connectionToken, int ackId)
{
ConnectionToken = connectionToken ?? throw new ArgumentNullException(nameof(connectionToken));
AckId = ackId;
}
}

/// <summary>
/// A server-bound message that pushes refreshed user claims of a client connection to its owning app server.
/// </summary>
public class UpdateConnectionClaimsMessage : ExtensibleServiceMessage
{
/// <summary>
/// Gets or sets the connection id of the live client connection whose claims are being updated.
/// </summary>
public string ConnectionId { get; set; }

/// <summary>
/// Gets or sets the refreshed user claims to apply on the owning app server.
/// </summary>
public System.Security.Claims.Claim[]? Claims { get; set; }

/// <summary>
/// Initializes a new instance of the <see cref="UpdateConnectionClaimsMessage"/> class.
/// </summary>
/// <param name="connectionId">The connection id of the live client connection.</param>
/// <param name="claims">The refreshed user claims to apply on the owning app server.</param>
public UpdateConnectionClaimsMessage(string connectionId, System.Security.Claims.Claim[]? claims)
{
ConnectionId = connectionId ?? throw new ArgumentNullException(nameof(connectionId));
Claims = claims;
}
}

/// <summary>
/// A handshake request message.
/// </summary>
Expand Down
58 changes: 58 additions & 0 deletions src/Microsoft.Azure.SignalR.Protocols/ServiceProtocol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ public bool TryParseMessage(ref ReadOnlySequence<byte> input, out ServiceMessage
return CreateGroupMemberQueryMessage(ref reader, arrayLength);
case ServiceProtocolConstants.RefreshAuthMessageType:
return CreateRefreshAuthMessage(ref reader, arrayLength);
case ServiceProtocolConstants.GetConnectionClaimsMessageType:
return CreateGetConnectionClaimsMessage(ref reader, arrayLength);
case ServiceProtocolConstants.UpdateConnectionClaimsMessageType:
return CreateUpdateConnectionClaimsMessage(ref reader, arrayLength);
default:
// Future protocol changes can add message types, old clients can ignore them
return null;
Expand Down Expand Up @@ -348,6 +352,12 @@ private static void WriteMessageCore(ref MessagePackWriter writer, ServiceMessag
case RefreshAuthMessage refreshAuthMessage:
WriteRefreshAuthMessage(ref writer, refreshAuthMessage);
break;
case GetConnectionClaimsMessage getConnectionClaimsMessage:
WriteGetConnectionClaimsMessage(ref writer, getConnectionClaimsMessage);
break;
case UpdateConnectionClaimsMessage updateConnectionClaimsMessage:
WriteUpdateConnectionClaimsMessage(ref writer, updateConnectionClaimsMessage);
break;
default:
throw new InvalidDataException($"Unexpected message type: {message.GetType().Name}");
}
Expand Down Expand Up @@ -812,6 +822,36 @@ private static void WriteRefreshAuthMessage(ref MessagePackWriter writer, Refres
message.WriteExtensionMembers(ref writer);
}

private static void WriteGetConnectionClaimsMessage(ref MessagePackWriter writer, GetConnectionClaimsMessage message)
{
writer.WriteArrayHeader(4);
writer.Write(ServiceProtocolConstants.GetConnectionClaimsMessageType);
writer.Write(message.ConnectionToken);
writer.Write(message.AckId);
message.WriteExtensionMembers(ref writer);
}

private static void WriteUpdateConnectionClaimsMessage(ref MessagePackWriter writer, UpdateConnectionClaimsMessage message)
{
writer.WriteArrayHeader(4);
writer.Write(ServiceProtocolConstants.UpdateConnectionClaimsMessageType);
writer.Write(message.ConnectionId);
if (message.Claims?.Length > 0)
{
writer.WriteMapHeader(message.Claims.Length);
foreach (var claim in message.Claims)
{
writer.Write(claim.Type);
writer.Write(claim.Value);
}
}
else
{
writer.WriteMapHeader(0);
}
message.WriteExtensionMembers(ref writer);
}

private static void WriteStringArray(ref MessagePackWriter writer, IReadOnlyList<string>? array)
{
if (array?.Count > 0)
Expand Down Expand Up @@ -1463,4 +1503,22 @@ private static RefreshAuthMessage CreateRefreshAuthMessage(ref MessagePackReader
return message;
}

private static GetConnectionClaimsMessage CreateGetConnectionClaimsMessage(ref MessagePackReader reader, int arrayLength)
{
var connectionToken = ReadStringNotNull(ref reader, "connectionToken");
var ackId = ReadInt32(ref reader, "ackId");
var message = new GetConnectionClaimsMessage(connectionToken, ackId);
message.ReadExtensionMembers(ref reader);
return message;
}

private static UpdateConnectionClaimsMessage CreateUpdateConnectionClaimsMessage(ref MessagePackReader reader, int arrayLength)
{
var connectionId = ReadStringNotNull(ref reader, "connectionId");
var claims = ReadClaims(ref reader);
var message = new UpdateConnectionClaimsMessage(connectionId, claims);
message.ReadExtensionMembers(ref reader);
return message;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ public static class ServiceProtocolConstants
public const int ConnectionFlowControlMessageType = 39;
public const int GroupMemberQueryMessageType = 40;
public const int RefreshAuthMessageType = 41;
public const int GetConnectionClaimsMessageType = 42;
public const int UpdateConnectionClaimsMessageType = 43;
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ public bool Equals(ServiceMessage x, ServiceMessage y)
return GroupMemberQueryMessageEqual(groupMemberQueryMessage, (GroupMemberQueryMessage)y);
case RefreshAuthMessage refreshAuthMessage:
return RefreshAuthMessageEqual(refreshAuthMessage, (RefreshAuthMessage)y);
case GetConnectionClaimsMessage getConnectionClaimsMessage:
return GetConnectionClaimsMessageEqual(getConnectionClaimsMessage, (GetConnectionClaimsMessage)y);
case UpdateConnectionClaimsMessage updateConnectionClaimsMessage:
return UpdateConnectionClaimsMessageEqual(updateConnectionClaimsMessage, (UpdateConnectionClaimsMessage)y);
default:
throw new InvalidOperationException($"Unknown message type: {x.GetType().FullName}");
}
Expand Down Expand Up @@ -420,6 +424,18 @@ private static bool RefreshAuthMessageEqual(RefreshAuthMessage x, RefreshAuthMes
x.ExpireTime.UtcDateTime == y.ExpireTime.UtcDateTime;
}

private static bool GetConnectionClaimsMessageEqual(GetConnectionClaimsMessage x, GetConnectionClaimsMessage y)
{
return StringEqual(x.ConnectionToken, y.ConnectionToken) &&
x.AckId == y.AckId;
}

private static bool UpdateConnectionClaimsMessageEqual(UpdateConnectionClaimsMessage x, UpdateConnectionClaimsMessage y)
{
return StringEqual(x.ConnectionId, y.ConnectionId) &&
ClaimsEqual(x.Claims, y.Claims);
}

private static bool StringEqual(string x, string y)
{
return string.Equals(x, y, StringComparison.Ordinal);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,17 @@ public static IEnumerable<object[]> TestParseOldData
new System.Security.Claims.Claim("role", "reader"),
}, new DateTimeOffset(2024, 1, 1, 0, 0, 0, TimeSpan.Zero), 1),
binary: "limlY29ubjGBpHJvbGWmcmVhZGVy1v9lkgCAAYA="),
new ProtocolTestData(
name: "GetConnectionClaimsMessage",
message: new GetConnectionClaimsMessage("conn1", 1),
binary: "lCqlY29ubjEBgA=="),
new ProtocolTestData(
name: "UpdateConnectionClaimsMessage",
message: new UpdateConnectionClaimsMessage("conn1", new[]
{
new System.Security.Claims.Claim("role", "reader"),
}),
binary: "lCulY29ubjGBpHJvbGWmcmVhZGVygA=="),
}.ToDictionary(t => t.Name);

#pragma warning restore CS0618 // Type or member is obsolete
Expand Down
Loading