Skip to content

Commit

Permalink
ADR 019 password rotation (#725)
Browse files Browse the repository at this point in the history
This PR updates the preview feature "re-auth" significantly. The changes allow for catering to a wider range of use cases including simple password rotation.

As part of this PR, all auth-related namespaces have been moved to preview - previously some did not have this, although the classes therein would not have been usable.

Since this is a preview feature all changes here are breaking changes.

The OnTokenExpiredAsync method in the IAuthTokenManager interface was removed, and a new HandleSecurityExceptionAsync method was added in its place.

The ExpirationBased method in AuthTokenManagers was renamed to Bearer, and a new Basic method was added to cater for password rotation.

---------

Co-authored-by: grant lodge <6323995+thelonelyvulpes@users.noreply.github.com>
RichardIrons-neo4j and thelonelyvulpes authored Aug 22, 2023
1 parent 7c93291 commit 6f36b08
Showing 46 changed files with 542 additions and 429 deletions.
Original file line number Diff line number Diff line change
@@ -19,9 +19,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using FluentAssertions;
using Neo4j.Driver.Auth;
using Neo4j.Driver.IntegrationTests.Internals;
using Neo4j.Driver.Internal;
using Neo4j.Driver.Internal.Auth;
using Xunit;
using Xunit.Abstractions;
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@
using System.Net;
using System.Threading.Tasks;
using FluentAssertions;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;
using Neo4j.Driver.IntegrationTests.Internals;
using Neo4j.Driver.Internal;
using Neo4j.Driver.Internal.Auth;
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
// limitations under the License.

using System;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;
using Neo4j.Driver.IntegrationTests.Internals;
using Xunit;
using Xunit.Abstractions;
2 changes: 1 addition & 1 deletion Neo4j.Driver/Neo4j.Driver.Tests.Integration/Examples.cs
Original file line number Diff line number Diff line change
@@ -22,7 +22,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using FluentAssertions;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;
using Neo4j.Driver.IntegrationTests.Internals;
using Xunit;
using Xunit.Abstractions;
Original file line number Diff line number Diff line change
@@ -20,7 +20,7 @@
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;

namespace Neo4j.Driver.IntegrationTests.Internals;

Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@

using System;
using Castle.Core.Internal;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;

namespace Neo4j.Driver.IntegrationTests.Internals;

Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
// limitations under the License.

using System;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;

namespace Neo4j.Driver.IntegrationTests.Internals;

Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@

using System;
using System.IO;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;

namespace Neo4j.Driver.IntegrationTests.Internals;

Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
// limitations under the License.

using System;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;

namespace Neo4j.Driver.IntegrationTests.Internals;

Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@

using System;
using System.Diagnostics;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;
using Neo4j.Driver.TestUtil;

namespace Neo4j.Driver.IntegrationTests.Internals;
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@

using System;
using System.Linq;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;
using Org.BouncyCastle.Pkcs;

namespace Neo4j.Driver.IntegrationTests.Internals;
Original file line number Diff line number Diff line change
@@ -16,7 +16,7 @@
// limitations under the License.

using System;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;
using Neo4j.Driver.IntegrationTests.Internals;
using Neo4j.Driver.TestUtil;
using Xunit;
Original file line number Diff line number Diff line change
@@ -24,7 +24,7 @@
using System.Threading;
using System.Threading.Tasks;
using FluentAssertions;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Preview.Auth;
using Neo4j.Driver.IntegrationTests.Extensions;
using Neo4j.Driver.IntegrationTests.Internals;
using Neo4j.Driver.Internal;
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
// Copyright (c) "Neo4j"
// Neo4j Sweden AB [http://neo4j.com]
//
//
// This file is part of Neo4j.
//
//
// 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
//
//
// http://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.

using System.Threading.Tasks;
namespace Neo4j.Driver.Tests.TestBackend;

namespace Neo4j.Driver.Auth;

/// <summary>Provides auth tokens that expire.</summary>
public interface IExpiringAuthTokenProvider
internal class AuthTokenManagerHandleSecurityExceptionCompleted : IProtocolObject
{
/// <summary>Gets a new auth token and expiration time.</summary>
/// <returns>A task that represents the asynchronous operation.</returns>
Task<AuthTokenAndExpiration> GetTokenAsync();
public AuthTokenManagerHandleSecurityExceptionCompletedDto data { get; set; }

public class AuthTokenManagerHandleSecurityExceptionCompletedDto
{
public string requestId { get; set; }
public bool handled { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -15,14 +15,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Diagnostics;

namespace Neo4j.Driver.Tests.TestBackend;

internal class AuthTokenManagerOnAuthExpiredCompleted : IProtocolObject
internal class BasicAuthTokenProviderCompleted : IProtocolObject
{
public AuthTokenManagerOnExpiredCompletedDto data { get; set; }
public BasicAuthTokenProviderCompletedDto data { get; set; }

public class AuthTokenManagerOnExpiredCompletedDto
public class BasicAuthTokenProviderCompletedDto
{
public string requestId { get; set; }
public AuthorizationToken auth { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -19,11 +19,11 @@

namespace Neo4j.Driver.Tests.TestBackend;

internal class ExpirationBasedAuthTokenProviderCompleted : IProtocolObject
internal class BearerAuthTokenProviderCompleted : IProtocolObject
{
public ExpirationBasedAuthTokenProviderCompletedDto data { get; set; }
public BearerAuthTokenProviderCompletedDto data { get; set; }

public class ExpirationBasedAuthTokenProviderCompletedDto
public class BearerAuthTokenProviderCompletedDto
{
public string requestId { get; set; }
public AuthTokenAndExpirationDto auth { get; set; }
Original file line number Diff line number Diff line change
@@ -19,7 +19,6 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Neo4j.Driver.Auth;
using Neo4j.Driver.Internal;
using Neo4j.Driver.Internal.Auth;

@@ -41,7 +40,7 @@ public override string Respond()
return new ProtocolResponse("AuthTokenManager", uniqueId).Encode();
}

public override async Task<IAuthToken> GetTokenAsync(CancellationToken cancellationToken = default)
public override async ValueTask<IAuthToken> GetTokenAsync(CancellationToken cancellationToken = default)
{
var requestId = Guid.NewGuid().ToString();
await _controller.SendResponse(GetAuthRequest(requestId)).ConfigureAwait(false);
@@ -63,20 +62,25 @@ private string GetAuthRequest(string requestId)
new { authTokenManagerId = uniqueId, id = requestId }).Encode();
}

public override async Task OnTokenExpiredAsync(IAuthToken token, CancellationToken cancellationToken = default)
public override async ValueTask<bool> HandleSecurityExceptionAsync(
IAuthToken token,
SecurityException exception,
CancellationToken cancellationToken = default)
{
var requestId = Guid.NewGuid().ToString();
await _controller.SendResponse(GetAuthExpiredRequest(requestId, token)).ConfigureAwait(false);
var result = await _controller.TryConsumeStreamObjectOfType<AuthTokenManagerOnAuthExpiredCompleted>()
await _controller.SendResponse(GetAuthExpiredRequest(requestId, token, exception)).ConfigureAwait(false);
var result = await _controller.TryConsumeStreamObjectOfType<AuthTokenManagerHandleSecurityExceptionCompleted>()
.ConfigureAwait(false);

if (result.data.requestId != requestId)
{
throw new Exception("OnTokenExpiredAsync: request IDs did not match");
}

return result.data.handled;
}

private string GetAuthExpiredRequest(string requestId, IAuthToken token)
private string GetAuthExpiredRequest(string requestId, IAuthToken token, SecurityException exception)
{
if (token is not AuthToken authToken)
{
@@ -86,11 +90,12 @@ private string GetAuthExpiredRequest(string requestId, IAuthToken token)
var content = authToken.Content;

return new ProtocolResponse(
"AuthTokenManagerOnAuthExpiredRequest",
"AuthTokenManagerHandleSecurityExceptionRequest",
new
{
authTokenManagerId = uniqueId,
id = requestId,
errorCode = exception.Code,
auth = new
{
name = "AuthorizationToken",
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) "Neo4j"
// Neo4j Sweden AB [http://neo4j.com]
//
// This file is part of Neo4j.
//
// 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
//
// http://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.

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Neo4j.Driver.Preview.Auth;
using Neo4j.Driver.Internal;
using Neo4j.Driver.Internal.Auth;

namespace Neo4j.Driver.Tests.TestBackend;

internal abstract class TestAuthTokenManager : IProtocolObject, IAuthTokenManager
{
public abstract ValueTask<IAuthToken> GetTokenAsync(CancellationToken cancellationToken = default);

public abstract ValueTask<bool> HandleSecurityExceptionAsync(
IAuthToken token,
SecurityException exception,
CancellationToken cancellationToken = default);
}

internal class NewNeo4jAuthTokenManager : IProtocolObject
{
protected Controller _controller;
public IAuthTokenManager TokenManager;
}

internal class NewBasicAuthTokenManager : NewNeo4jAuthTokenManager
{
public object data { get; set; }

public override Task Process(Controller controller)
{
_controller = controller;
TokenManager = AuthTokenManagers.Basic(FakeTime.Instance, GetTokenAsync);
return Task.CompletedTask;
}

public async ValueTask<IAuthToken> GetTokenAsync()
{
var requestId = Guid.NewGuid().ToString();
await _controller.SendResponse(GetAuthRequest(requestId)).ConfigureAwait(false);
var result = await _controller.TryConsumeStreamObjectOfType<BasicAuthTokenProviderCompleted>()
.ConfigureAwait(false);

if (result.data.requestId == requestId)
{
var token = new AuthToken(result.data.auth.data.ToDictionary());
return token;
}

throw new Exception("GetTokenAsync: request IDs did not match");
}

public override string Respond()
{
return new ProtocolResponse("BasicAuthTokenManager", uniqueId).Encode();
}

protected string GetAuthRequest(string requestId)
{
return new ProtocolResponse(
"BasicAuthTokenProviderRequest",
new { basicAuthTokenManagerId = uniqueId, id = requestId }).Encode();
}
}
Loading

0 comments on commit 6f36b08

Please sign in to comment.