Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added new server events. #103

Merged
merged 3 commits into from
Dec 8, 2024
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
159 changes: 155 additions & 4 deletions CoreRemoting.Tests/RpcTests.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Data;
using System.Diagnostics;
using System.Security;
using System.Threading;
using System.Threading.Tasks;
using CoreRemoting.Authentication;
Expand Down Expand Up @@ -326,6 +327,7 @@ public void Generic_methods_should_be_called_correctly()
var result = proxy.Echo("Yay");

Assert.Equal("Yay", result);
Assert.Equal(0, _serverFixture.ServerErrorCount);
}

[Fact]
Expand All @@ -345,6 +347,7 @@ public void Inherited_methods_should_be_called_correctly()
var result = proxy.BaseMethod();

Assert.True(result);
Assert.Equal(0, _serverFixture.ServerErrorCount);
}

[Fact]
Expand All @@ -366,6 +369,7 @@ public void Enum_arguments_should_be_passed_correctly()

Assert.Equal(TestEnum.First, resultFirst);
Assert.Equal(TestEnum.Second, resultSecond);
Assert.Equal(0, _serverFixture.ServerErrorCount);
}

[Fact]
Expand Down Expand Up @@ -402,6 +406,7 @@ public void Missing_method_throws_RemoteInvocationException()
// a localized message similar to "Method 'Missing method' not found"
Assert.NotNull(ex);
Assert.Contains("Missing Method", ex.Message);
Assert.Equal(0, _serverFixture.ServerErrorCount);
}

[Fact]
Expand All @@ -425,6 +430,7 @@ public void Missing_service_throws_RemoteInvocationException()
// a localized message similar to "Service 'System.IDisposable' is not registered"
Assert.NotNull(ex);
Assert.Contains("IDisposable", ex.Message);
Assert.Equal(0, _serverFixture.ServerErrorCount);
}

[Fact]
Expand Down Expand Up @@ -596,6 +602,9 @@ async Task Roundtrip(bool encryption)
finally
{
_serverFixture.Server.Config.MessageEncryption = oldEncryption;

// reset the error counter for other tests
_serverFixture.ServerErrorCount = 0;
}
}

Expand Down Expand Up @@ -852,7 +861,7 @@ void RejectCall(object sender, ServerRpcContext e) =>
client.Connect();

var proxy = client.CreateProxy<IFailingService>();
var ex = Assert.Throws<RemoteInvocationException>(() => proxy.Hello());
var ex = Assert.Throws<RemoteInvocationException>(proxy.Hello);

// Session is not authenticated
Assert.Contains("authenticated", ex.Message);
Expand Down Expand Up @@ -888,7 +897,7 @@ public void Authentication_handler_has_access_to_the_current_session()
Channel = ClientChannel,
MessageEncryption = false,
ServerPort = _serverFixture.Server.Config.NetworkPort,
Credentials = [new Credential()],
Credentials = [new()],
});

client.Connect();
Expand All @@ -903,6 +912,43 @@ public void Authentication_handler_has_access_to_the_current_session()
}
}

[Fact]
public void Broken_auhentication_handler_doesnt_break_the_server()
{
var server = _serverFixture.Server;
var authProvider = server.Config.AuthenticationProvider;
server.Config.AuthenticationRequired = true;
server.Config.AuthenticationProvider = new FakeAuthProvider
{
AuthenticateFake = c => throw new Exception("Broken")
};

try
{
using var client = new RemotingClient(new ClientConfig()
{
ConnectionTimeout = 3,
InvocationTimeout = 3,
SendTimeout = 3,
Channel = ClientChannel,
MessageEncryption = false,
ServerPort = _serverFixture.Server.Config.NetworkPort,
Credentials = [new()],
});

var ex = Assert.Throws<SecurityException>(client.Connect);

Assert.Contains("auth", ex.Message.ToLower());
Assert.Contains("failed", ex.Message);
}
finally
{
server.Config.AuthenticationProvider = authProvider;
server.Config.AuthenticationRequired = false;
_serverFixture.ServerErrorCount = 0;
}
}

[Fact]
public void Authentication_handler_can_check_client_address()
{
Expand All @@ -913,8 +959,8 @@ public void Authentication_handler_can_check_client_address()
{
AuthenticateFake = c =>
{
var address = RemotingSession.Current.ClientAddress ??
throw new ArgumentNullException();
var address = RemotingSession.Current?.ClientAddress ??
throw new ArgumentNullException("ClientAddress");

// allow only localhost connections
return address.Contains("127.0.0.1") || // ipv4
Expand Down Expand Up @@ -967,5 +1013,110 @@ public void ServerComponent_can_track_client_network_address()
// what's my address as seen by remote server?
Assert.NotNull(proxy.ClientAddress);
}

[Fact]
public void Logon_and_logoff_events_are_triggered()
{
void CheckSession(string operation)
{
var rs = RemotingSession.Current;
Assert.NotNull(rs);
Assert.True(rs?.IsAuthenticated);
Assert.NotNull(rs?.ClientAddress);
Assert.NotNull(rs?.Identity);
Console.WriteLine($"Client {rs.Identity.Name} from {rs.ClientAddress} is {operation}");
}

var logon = false;
void Logon(object sender, EventArgs _)
{
logon = true;
CheckSession("logged on");
}

var logoff = false;
void Logoff(object sender, EventArgs _)
{
logoff = true;
CheckSession("logged off");
}

var server = _serverFixture.Server;
var authProvider = server.Config.AuthenticationProvider;
server.Config.AuthenticationProvider = new FakeAuthProvider();

server.Logon += Logon;
server.Logoff += Logoff;
server.Config.AuthenticationRequired = true;

try
{
using var client = new RemotingClient(new ClientConfig()
{
ConnectionTimeout = 0,
InvocationTimeout = 0,
SendTimeout = 0,
Channel = ClientChannel,
MessageEncryption = false,
Credentials = [new()],
ServerPort = _serverFixture.Server.Config.NetworkPort,
});

client.Connect();

var proxy = client.CreateProxy<ITestService>();
Assert.Equal("Hello", proxy.Echo("Hello"));

client.Disconnect();

Assert.True(logon);
Assert.True(logoff);
}
finally
{
server.Config.AuthenticationProvider = authProvider;
server.Config.AuthenticationRequired = false;
server.Logoff -= Logoff;
server.Logon -= Logon;
}
}

[Fact]
public void BeginCall_event_handler_can_bypass_authentication_for_chosen_method()
{
void BypassAuthorizationForEcho(object sender, ServerRpcContext e) =>
e.AuthenticationRequired =
e.MethodCallMessage.MethodName != "Echo";

_serverFixture.Server.Config.AuthenticationRequired = true;
_serverFixture.Server.BeginCall += BypassAuthorizationForEcho;
try
{
using var client = new RemotingClient(new ClientConfig()
{
ConnectionTimeout = 0,
InvocationTimeout = 0,
SendTimeout = 0,
Channel = ClientChannel,
MessageEncryption = false,
ServerPort = _serverFixture.Server.Config.NetworkPort,
});

client.Connect();

// try allowed method "Echo"
var proxy = client.CreateProxy<ITestService>();
Assert.Equal("This method is allowed", proxy.Echo("This method is allowed"));

// try disallowed method "Reverse"
var ex = Assert.Throws<RemoteInvocationException>(() => proxy.Reverse("This method is not allowed"));
Assert.Contains("auth", ex.Message);
}
finally
{
_serverFixture.Server.BeginCall -= BypassAuthorizationForEcho;
_serverFixture.Server.Config.AuthenticationRequired = false;
}
}
}
}
4 changes: 2 additions & 2 deletions CoreRemoting.Tests/Tools/FakeAuthProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public class FakeAuthProvider : IAuthenticationProvider

public bool Authenticate(Credential[] credentials, out RemotingIdentity authenticatedIdentity)
{
var success = AuthenticateFake(credentials);
var success = AuthenticateFake?.Invoke(credentials) ?? true;

authenticatedIdentity =
new RemotingIdentity()
Expand All @@ -18,7 +18,7 @@ public bool Authenticate(Credential[] credentials, out RemotingIdentity authenti
Domain = "domain",
IsAuthenticated = success,
Name = credentials[0].Value,
Roles = new[] {"Test"}
Roles = ["Test"],
};

return success;
Expand Down
15 changes: 8 additions & 7 deletions CoreRemoting/Channels/Tcp/TcpClientChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public class TcpClientChannel : IClientChannel, IRawMessageTransport
/// Event: Fires when a message is received from server.
/// </summary>
public event Action<byte[]> ReceiveMessage;

/// <summary>
/// Event: Fires when an error is occurred.
/// </summary>
Expand All @@ -34,7 +34,7 @@ public void Init(IRemotingClient client)
_tcpClient = new WatsonTcpClient(client.Config.ServerHostName, client.Config.ServerPort);
_tcpClient.Settings.NoDelay = true;

_handshakeMetadata =
_handshakeMetadata =
new Dictionary<string, object>
{
{ "MessageEncryption", client.MessageEncryption }
Expand All @@ -51,7 +51,7 @@ public void Connect()
{
if (_tcpClient == null)
throw new InvalidOperationException("Channel is not initialized.");

if (_tcpClient.Connected)
return;

Expand All @@ -60,6 +60,7 @@ public void Connect()
_tcpClient.Events.ServerDisconnected += OnDisconnected;
_tcpClient.Connect();
}

private void OnDisconnected(object o, DisconnectionEventArgs disconnectionEventArgs)
{
Disconnected?.Invoke();
Expand All @@ -73,7 +74,7 @@ private void OnDisconnected(object o, DisconnectionEventArgs disconnectionEventA
private void OnError(object sender, ExceptionEventArgs e)
{
LastException = new NetworkException(e.Exception.Message, e.Exception);

ErrorOccured?.Invoke(e.Exception.Message, e.Exception);
}

Expand Down Expand Up @@ -131,7 +132,7 @@ public void Disconnect()
/// Gets the raw message transport component for this connection.
/// </summary>
public IRawMessageTransport RawMessageTransport => this;

/// <summary>
/// Sends a message to the server.
/// </summary>
Expand All @@ -149,12 +150,12 @@ public bool SendMessage(byte[] rawMessage)
else
throw new NetworkException("Channel disconnected");
}

/// <summary>
/// Gets or sets the last exception.
/// </summary>
public NetworkException LastException { get; set; }

/// <summary>
/// Disconnect and free manages resources.
/// </summary>
Expand Down
16 changes: 8 additions & 8 deletions CoreRemoting/Channels/Tcp/TcpServerChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public TcpServerChannel()
{
_connections = new ConcurrentDictionary<Guid, TcpConnection>();
}

/// <summary>
/// Initializes the channel.
/// </summary>
Expand All @@ -45,11 +45,11 @@ private void OnClientConnected(object sender, ConnectionEventArgs e)
{
{ "ServerAcceptConnection", true }
};

_tcpServer.SendAsync(e.Client.Guid, new byte[]{ 0x02 }, metadata);
}
}

private void OnClientDisconnected(object sender, DisconnectionEventArgs e)
{
_connections.TryRemove(e.Client.Guid, out _);
Expand All @@ -59,7 +59,7 @@ private void OnTcpMessageReceived(object sender, MessageReceivedEventArgs e)
{
if (_connections.TryGetValue(e.Client.Guid, out TcpConnection connection))
{
connection.FireReceiveMessage(e.Data, e.Metadata);
connection.FireReceiveMessage(e.Data, e.Metadata);
}
}

Expand All @@ -70,7 +70,7 @@ public void StartListening()
{
if (_tcpServer == null)
throw new InvalidOperationException("Channel is not initialized.");

_tcpServer.Start();
}

Expand All @@ -81,16 +81,16 @@ public void StopListening()
{
if (_tcpServer == null)
return;

_tcpServer.Stop();
}

/// <summary>
/// Gets whether the channel is listening or not.
/// </summary>
public bool IsListening =>
public bool IsListening =>
_tcpServer?.IsListening ?? false;

/// <summary>
/// Stops listening and frees managed resources.
/// </summary>
Expand Down
Loading
Loading