Skip to content

Commit 2bdd302

Browse files
committed
Merged with master
2 parents a988ad8 + a1ee19c commit 2bdd302

15 files changed

+571
-9
lines changed

FirebaseAdmin/FirebaseAdmin.IntegrationTests/FirebaseAdmin.IntegrationTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="Google.Apis.Auth" Version="1.35.1" />
11+
<PackageReference Include="Google.Apis.Auth" Version="1.40.0" />
1212
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.7.0" />
1313
<PackageReference Include="xunit" Version="2.3.1" />
1414
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />

FirebaseAdmin/FirebaseAdmin.Snippets/FirebaseAdmin.Snippets.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
</PropertyGroup>
99

1010
<ItemGroup>
11-
<PackageReference Include="Google.Apis.Auth" Version="1.35.1" />
11+
<PackageReference Include="Google.Apis.Auth" Version="1.40.0" />
1212
</ItemGroup>
1313

1414
<ItemGroup>

FirebaseAdmin/FirebaseAdmin.Tests/Auth/FirebaseUserManagerTest.cs

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@
1414

1515
using System;
1616
using System.Collections.Generic;
17+
using System.Linq;
1718
using System.Net;
1819
using System.Net.Http;
1920
using System.Threading.Tasks;
2021
using FirebaseAdmin.Tests;
22+
using Google.Api.Gax;
23+
using Google.Api.Gax.Rest;
2124
using Google.Apis.Auth.OAuth2;
2225
using Google.Apis.Json;
2326
using Newtonsoft.Json.Linq;
@@ -296,6 +299,110 @@ public async Task GetUserByPhoneNumberEmpty()
296299
await Assert.ThrowsAsync<ArgumentException>(() => userManager.GetUserByPhoneNumberAsync(string.Empty));
297300
}
298301

302+
[Fact]
303+
public async Task ListUsersPaged()
304+
{
305+
var handler = new MockMessageHandler()
306+
{
307+
Response = new List<string>()
308+
{
309+
@"{
310+
""nextPageToken"": ""token"",
311+
""users"": [
312+
{""localId"": ""user1""},
313+
{""localId"": ""user2""},
314+
{""localId"": ""user3""}
315+
]
316+
}",
317+
@"{
318+
""users"": [
319+
{""localId"": ""user4""},
320+
{""localId"": ""user5""},
321+
{""localId"": ""user6""}
322+
]
323+
}",
324+
},
325+
};
326+
var userManager = this.CreateFirebaseUserManager(handler);
327+
328+
var usersPage = userManager.ListUsers(new ListUsersOptions());
329+
330+
var users = new List<ExportedUserRecord>();
331+
var tokens = new List<string>();
332+
var pageCounter = 0;
333+
for (Page<ExportedUserRecord> userPage; (userPage = await usersPage.ReadPageAsync(3)) != null;)
334+
{
335+
pageCounter++;
336+
tokens.Add(userPage.NextPageToken);
337+
users.AddRange(userPage);
338+
if (string.IsNullOrEmpty(userPage.NextPageToken))
339+
{
340+
break;
341+
}
342+
}
343+
344+
Assert.Equal(2, pageCounter);
345+
Assert.Equal(6, users.Count);
346+
Assert.Equal("token", tokens[0]);
347+
Assert.Null(tokens[1]);
348+
Assert.Equal("user1", users[0].Uid);
349+
Assert.Equal("user2", users[1].Uid);
350+
Assert.Equal("user3", users[2].Uid);
351+
Assert.Equal("user4", users[3].Uid);
352+
Assert.Equal("user5", users[4].Uid);
353+
Assert.Equal("user6", users[5].Uid);
354+
}
355+
356+
[Fact]
357+
public async Task ListUsers()
358+
{
359+
var nextPageToken = Guid.NewGuid().ToString();
360+
var handler = new MockMessageHandler()
361+
{
362+
Response = new DownloadAccountResponse()
363+
{
364+
NextPageToken = nextPageToken,
365+
Users = new List<GetAccountInfoResponse.User>()
366+
{
367+
new GetAccountInfoResponse.User() { UserId = "user1" },
368+
new GetAccountInfoResponse.User() { UserId = "user2" },
369+
new GetAccountInfoResponse.User() { UserId = "user3" },
370+
},
371+
},
372+
};
373+
374+
var userManager = this.CreateFirebaseUserManager(handler);
375+
376+
var usersPage = userManager.ListUsers(new ListUsersOptions());
377+
var listUsersRequest = await usersPage.ReadPageAsync(3);
378+
var userRecords = listUsersRequest.ToList();
379+
Assert.Equal(nextPageToken, listUsersRequest.NextPageToken);
380+
Assert.Equal(3, userRecords.Count);
381+
Assert.Equal("user1", userRecords[0].Uid);
382+
Assert.Equal("user2", userRecords[1].Uid);
383+
Assert.Equal("user3", userRecords[2].Uid);
384+
}
385+
386+
[Fact]
387+
public void ListUsersRequestOptionsAreSet()
388+
{
389+
var userManager = this.CreateFirebaseUserManager(new MockMessageHandler());
390+
391+
var listUsersRequest = userManager.CreateListUserRequest(new ListUsersOptions());
392+
393+
// by default they are set
394+
Assert.True(listUsersRequest.RequestParameters.ContainsKey("maxResults"));
395+
Assert.True(listUsersRequest.RequestParameters.ContainsKey("nextPageToken"));
396+
Assert.Equal(FirebaseUserManager.MaxListUsersResults, int.Parse(listUsersRequest.RequestParameters["maxResults"].DefaultValue));
397+
Assert.Null(listUsersRequest.RequestParameters["nextPageToken"].DefaultValue);
398+
399+
// change the values and check again
400+
listUsersRequest.SetPageSize(10);
401+
listUsersRequest.SetPageToken("theNewNextPageToken");
402+
Assert.Equal(10, int.Parse(listUsersRequest.RequestParameters["maxResults"].DefaultValue));
403+
Assert.Equal("theNewNextPageToken", listUsersRequest.RequestParameters["nextPageToken"].DefaultValue);
404+
}
405+
299406
[Fact]
300407
public async Task CreateUser()
301408
{

FirebaseAdmin/FirebaseAdmin.Tests/FirebaseAdmin.Tests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
</PropertyGroup>
1111

1212
<ItemGroup>
13-
<PackageReference Include="Google.Apis.Auth" Version="1.35.1" />
13+
<PackageReference Include="Google.Apis.Auth" Version="1.40.0" />
1414
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.6.0" />
1515
<PackageReference Include="xunit" Version="2.3.1" />
1616
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2019, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
namespace FirebaseAdmin.Auth
16+
{
17+
/// <summary>
18+
/// Contains metadata associated with a Firebase user account, along with password hash and salt.
19+
/// Instances of this class are immutable and thread safe.
20+
/// </summary>
21+
public sealed class ExportedUserRecord : UserRecord
22+
{
23+
private readonly string passwordHash;
24+
private readonly string passwordSalt;
25+
26+
internal ExportedUserRecord(GetAccountInfoResponse.User user)
27+
: base(user)
28+
{
29+
this.passwordHash = user.PasswordHash;
30+
this.passwordSalt = user.PasswordSalt;
31+
}
32+
33+
/// <summary>
34+
/// Gets the user's password hash as a base64-encoded string.
35+
/// If the Firebase Auth hashing algorithm (SCRYPT) was used to create the user account,
36+
/// returns the base64-encoded password hash of the user.If a different hashing algorithm was
37+
/// used to create this user, as is typical when migrating from another Auth system, returns
38+
/// an empty string. Returns null if no password is set.
39+
/// </summary>
40+
public string PasswordHash
41+
{
42+
get => this.passwordHash;
43+
}
44+
45+
/// <summary>
46+
/// Gets the user's password salt as a base64-encoded string.
47+
/// If the Firebase Auth hashing algorithm (SCRYPT) was used to create the user account,
48+
/// returns the base64-encoded password salt of the user.If a different hashing algorithm was
49+
/// used to create this user, as is typical when migrating from another Auth system, returns
50+
/// an empty string. Returns null if no password is set.
51+
/// </summary>
52+
public string PasswordSalt
53+
{
54+
get => this.passwordSalt;
55+
}
56+
}
57+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2019, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Collections.Generic;
16+
17+
namespace FirebaseAdmin.Auth
18+
{
19+
/// <summary>
20+
/// Contains a collection of Firebase user accounts.
21+
/// </summary>
22+
public sealed class ExportedUserRecords
23+
{
24+
/// <summary>
25+
/// Gets or sets the next page link.
26+
/// </summary>
27+
public string NextPageToken { get; set; }
28+
29+
/// <summary>
30+
/// Gets or sets the users.
31+
/// </summary>
32+
public List<ExportedUserRecord> Users { get; set; }
33+
}
34+
}

FirebaseAdmin/FirebaseAdmin/Auth/FirebaseAuth.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
using System.Collections.Generic;
1717
using System.Threading;
1818
using System.Threading.Tasks;
19+
using Google.Api.Gax;
20+
using Google.Api.Gax.Rest;
1921

2022
namespace FirebaseAdmin.Auth
2123
{
@@ -523,6 +525,18 @@ public async Task SetCustomUserClaimsAsync(
523525
await userManager.UpdateUserAsync(user, cancellationToken).ConfigureAwait(false);
524526
}
525527

528+
/// <summary>
529+
/// Gets an async enumerable to page users starting from the specified pageToken. If the pageToken is empty, it starts from the first page.
530+
/// </summary>
531+
/// <param name="requestOptions">The options for the next remote call.</param>
532+
/// <returns>A <see cref="PagedAsyncEnumerable{DownloadAccountResponse, UserRecord}"/> instance.</returns>
533+
public PagedAsyncEnumerable<ExportedUserRecords, ExportedUserRecord> ListUsersAsync(ListUsersOptions requestOptions)
534+
{
535+
var userManager = this.IfNotDeleted(() => this.userManager.Value);
536+
537+
return userManager.ListUsers(requestOptions);
538+
}
539+
526540
/// <summary>
527541
/// Deletes this <see cref="FirebaseAuth"/> service instance.
528542
/// </summary>

FirebaseAdmin/FirebaseAdmin/Auth/FirebaseUserManager.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
using System.Net.Http;
1818
using System.Threading;
1919
using System.Threading.Tasks;
20-
using Google.Apis.Auth.OAuth2;
20+
using Google.Api.Gax;
21+
using Google.Api.Gax.Rest;
2122
using Google.Apis.Http;
2223
using Google.Apis.Json;
2324
using Google.Apis.Util;
@@ -33,6 +34,8 @@ namespace FirebaseAdmin.Auth
3334
/// </summary>
3435
internal class FirebaseUserManager : IDisposable
3536
{
37+
public const int MaxListUsersResults = 1000;
38+
3639
private const string IdTooklitUrl = "https://identitytoolkit.googleapis.com/v1/projects/{0}";
3740

3841
private readonly ConfigurableHttpClient httpClient;
@@ -138,6 +141,15 @@ internal async Task<UserRecord> GetUserByPhoneNumberAsync(
138141
return await this.GetUserAsync(query, cancellationToken);
139142
}
140143

144+
internal PagedAsyncEnumerable<ExportedUserRecords, ExportedUserRecord> ListUsers(ListUsersOptions requestOptions)
145+
{
146+
var restPagedAsyncEnumerable = new RestPagedAsyncEnumerable<ListUsersRequest, ExportedUserRecords, ExportedUserRecord>(
147+
() => this.CreateListUserRequest(requestOptions),
148+
new ListUsersPageManager());
149+
150+
return restPagedAsyncEnumerable;
151+
}
152+
141153
/// <summary>
142154
/// Create a new user account.
143155
/// </summary>
@@ -208,6 +220,11 @@ internal async Task DeleteUserAsync(
208220
}
209221
}
210222

223+
internal ListUsersRequest CreateListUserRequest(ListUsersOptions requestOptions)
224+
{
225+
return new ListUsersRequest(this.baseUrl, this.httpClient, requestOptions);
226+
}
227+
211228
private async Task<UserRecord> GetUserAsync(UserQuery query, CancellationToken cancellationToken)
212229
{
213230
var response = await this.PostAndDeserializeAsync<GetAccountInfoResponse>(

FirebaseAdmin/FirebaseAdmin/Auth/GetAccountInfoResponse.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System;
2-
using System.Collections.Generic;
3-
using System.Text;
1+
using System.Collections.Generic;
42
using Newtonsoft.Json;
53

64
namespace FirebaseAdmin.Auth
@@ -39,6 +37,18 @@ internal sealed class User
3937
[JsonProperty(PropertyName = "email")]
4038
public string Email { get; set; }
4139

40+
/// <summary>
41+
/// Gets or sets the user's password hash.
42+
/// </summary>
43+
[JsonProperty(PropertyName = "passwordHash")]
44+
public string PasswordHash { get; set; }
45+
46+
/// <summary>
47+
/// Gets or sets the user's password salt.
48+
/// </summary>
49+
[JsonProperty(PropertyName = "salt")]
50+
public string PasswordSalt { get; set; }
51+
4252
/// <summary>
4353
/// Gets or sets the user's phone number.
4454
/// </summary>
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2018, Google Inc. All rights reserved.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System.Collections.Generic;
16+
using Newtonsoft.Json;
17+
18+
namespace FirebaseAdmin.Auth
19+
{
20+
/// <summary>
21+
/// JSON data binding for downloadAccountResponse messages sent by Google identity toolkit service.
22+
/// </summary>
23+
internal class DownloadAccountResponse
24+
{
25+
/// <summary>
26+
/// Gets or sets the next page link.
27+
/// </summary>
28+
[JsonProperty("nextPageToken")]
29+
public string NextPageToken { get; set; }
30+
31+
/// <summary>
32+
/// Gets or sets the users.
33+
/// </summary>
34+
[JsonProperty("users")]
35+
public List<GetAccountInfoResponse.User> Users { get; set; }
36+
}
37+
}

0 commit comments

Comments
 (0)