Skip to content

Commit

Permalink
Final fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
papafe committed Nov 3, 2023
1 parent c422cfb commit 57688ea
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## vNext (TBD)

### Enhancements
* Added the `App.EmailPasswordAuth.RetryCustomConfirmationAsync` method to be able to run again the confirmation function on the server for a given email. (Issue [#3463](https://github.com/realm/realm-dotnet/issues/3463))
* Added `User.Changed` event that can be used to notify subscribers that something about the user changed - typically this would be the user state or the access token. (Issue [#3429](https://github.com/realm/realm-dotnet/issues/3429))
* Added support for customizing the ignore attribute applied on certain generated properties of Realm models. The configuration option is called `realm.custom_ignore_attribute` and can be set in a global configuration file (more information about global configuration files can be found in the [.NET documentation](https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/configuration-files)). The Realm generator will treat this as an opaque string, that will be appended to the `IgnoreDataMember` and `XmlIgnore` attributes already applied on these members. The attributes must be fully qualified unless the namespace they reside in is added to a global usings file. For example, this is how you would add `JsonIgnore` from `System.Text.Json`:

Expand Down
2 changes: 1 addition & 1 deletion Realm/Realm/Handles/AppHandle.EmailPassword.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public async Task RetryCustomConfirmationAsync(string email)

try
{
EmailNativeMethods.resend_confirmation_email(_appHandle, email, (IntPtr)email.Length, GCHandle.ToIntPtr(tcsHandle), out var ex);
EmailNativeMethods.retry_custom_comfirmation(_appHandle, email, (IntPtr)email.Length, GCHandle.ToIntPtr(tcsHandle), out var ex);
ex.ThrowIfNecessary();
await tcs.Task;
}
Expand Down
4 changes: 2 additions & 2 deletions Realm/Realm/Sync/App.cs
Original file line number Diff line number Diff line change
Expand Up @@ -412,15 +412,15 @@ public Task ResendConfirmationEmailAsync(string email)
/// Rerun the custom confirmation function for the given mail.
/// </summary>
/// <param name="email">The email of the user.</param>
/// <returns> //TODO Finish the docs
/// <returns>
/// An awaitable <see cref="Task"/> representing the asynchronous request to the server that the custom confirmation function is run again. Successful
/// completion indicates that the user has been confirmed on the server.
/// </returns>
public Task RetryCustomConfirmationAsync(string email)
{
Argument.NotNullOrEmpty(email, nameof(email));

return _app.Handle.EmailPassword.ResendConfirmationEmailAsync(email);
return _app.Handle.EmailPassword.RetryCustomConfirmationAsync(email);
}

/// <summary>
Expand Down
2 changes: 2 additions & 0 deletions Tests/Realm.Tests/Sync/SyncTestHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ public static void RunBaasTestAsync(Func<Task> testFunc, int timeout = 30000)

public static string GetVerifiedUsername() => $"realm_tests_do_autoverify-{Guid.NewGuid()}";

public static string GetUnconfirmedUsername() => $"realm_tests_do_not_confirm-{Guid.NewGuid()}@g.it";

public static async Task TriggerClientResetOnServer(SyncConfigurationBase config)
{
var userId = config.User.Id;
Expand Down
36 changes: 36 additions & 0 deletions Tests/Realm.Tests/Sync/UserManagementTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -400,6 +400,42 @@ public void User_LinkCredentials_WhenInUse_Throws()
});
}

[Test]
public void User_RetryCustomConfirmationAsync_WorksInAllScenarios()
{
SyncTestHelpers.RunBaasTestAsync(async () =>
{
// Standard case
var unconfirmedMail = SyncTestHelpers.GetUnconfirmedUsername();
var credentials = Credentials.EmailPassword(unconfirmedMail, SyncTestHelpers.DefaultPassword);

// The first time the confirmation function is called we return "pending", so the user needs to be confirmed.
// At the same time we save the user email in a collection.
await DefaultApp.EmailPasswordAuth.RegisterUserAsync(unconfirmedMail, SyncTestHelpers.DefaultPassword).Timeout(10_000, detail: "Failed to register user");

var ex3 = await TestHelpers.AssertThrows<AppException>(() => DefaultApp.LogInAsync(credentials));
Assert.That(ex3.Message, Does.Contain("confirmation required"));

// The second time we call the confirmation function we find the email we saved in the collection and return "success", so the user
// gets confirmed and can log in.
await DefaultApp.EmailPasswordAuth.RetryCustomConfirmationAsync(unconfirmedMail);
var user = await DefaultApp.LogInAsync(credentials);
Assert.That(user.State, Is.EqualTo(UserState.LoggedIn));

// Logged in user case
var loggedInUser = await GetUserAsync();
var ex = await TestHelpers.AssertThrows<AppException>(() => DefaultApp.EmailPasswordAuth.RetryCustomConfirmationAsync(loggedInUser.Profile.Email!));
Assert.That(ex.StatusCode, Is.EqualTo(HttpStatusCode.BadRequest));
Assert.That(ex.Message, Does.Contain("already confirmed"));

// Unknown user case
var invalidEmail = "[email protected]";
var ex2 = await TestHelpers.AssertThrows<AppException>(() => DefaultApp.EmailPasswordAuth.RetryCustomConfirmationAsync(invalidEmail));
Assert.That(ex2.StatusCode, Is.EqualTo(HttpStatusCode.NotFound));
Assert.That(ex2.Message, Does.Contain("user not found"));
});
}

[Test]
public void User_JWT_LogsInAndReadsDataFromToken()
{
Expand Down
28 changes: 25 additions & 3 deletions Tools/DeployApps/BaasClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,34 @@ public class FunctionReturn
}

private const string ConfirmFuncSource =
@"exports = ({ token, tokenId, username }) => {
@"exports = async function ({ token, tokenId, username }) {
// process the confirm token, tokenId and username
if (username.includes(""realm_tests_do_autoverify"")) {
return { status: 'success' }
return { status: 'success' };
}
if (username.includes(""realm_tests_do_not_confirm"")) {
const mongodb = context.services.get('BackingDB');
let collection = mongodb.db('test_db').collection('not_confirmed');
let result = await collection.findOne({'email': username});
if(result === null)
{
let newVal = {
'email': username,
'token': token,
'tokenId': tokenId,
}
await collection.insertOne(newVal);
return { status: 'pending' };
}
return { status: 'success' };
}
// do not confirm the user
// fail the user confirmation
return { status: 'fail' };
};";

Expand Down

0 comments on commit 57688ea

Please sign in to comment.