Skip to content

Commit

Permalink
feat: add permissions v2 (#83)
Browse files Browse the repository at this point in the history
Co-authored-by: peterdeme <peter.deme@getstream.io>
  • Loading branch information
peterdeme and peterdeme committed Dec 20, 2021
1 parent ce57f84 commit 26fb0f5
Show file tree
Hide file tree
Showing 21 changed files with 590 additions and 55 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ nuget.exe
.vs/
.vscode/
.envrc
.runsettings
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,20 @@ Console.WriteLine("{0} reacted to msg id {1} with {2}", response.Reaction.User.I
await chan.AddModerators(new string[] { june.ID });
await chan.DemoteModerators(new string[] { june.ID });

// create roles
await client.Permissions.CreateRole("channel-boss");

// assign users to roles (optional message)
await chan.AssignRoles(new AssignRoleRequest
{
AssignRoles = new List<RoleAssignment>
{
new RoleAssignment { UserId = bob.ID, ChannelRole = Role.ChannelModerator },
new RoleAssignment { UserId = june.ID, ChannelRole = Role.ChannelModerator }
},
Message = new MessageInput { Text = "Bob and June just became mods", User = bob }
});

//add a ban with a timeout
await chan.BanUser(june.ID, "Being a big jerk", 4);

Expand Down
9 changes: 9 additions & 0 deletions commitlint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const Configuration = {
extends: ['@commitlint/config-conventional'],
/*
* Functions that return true if commitlint should ignore the given message.
* In our case, if the commit contains a large link, sometimes it cannot fit into
* the max line length, so let's ignore it.
*/
ignores: [(commit) => commit.includes('https://')],
}
38 changes: 35 additions & 3 deletions src/stream-chat-net-test/ChannelTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
using System.Threading.Tasks;
using StreamChat;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace StreamChatTests
{
[Parallelizable(ParallelScope.None)]
Expand Down Expand Up @@ -1030,5 +1027,40 @@ public async Task TestUnbanUser()
await channel.BanUser(user2.ID, user1.ID, Guid.NewGuid().ToString(), 3);
await channel.UnbanUser(user2.ID);
}

[Test]
public async Task AssignRolesAsync()
{
var user1 = new User()
{
ID = Guid.NewGuid().ToString(),
Role = Role.Admin,
};

var user2 = new User()
{
ID = Guid.NewGuid().ToString(),
Role = Role.User,
};

var members = new User[] { user1, user2 };

await this._client.Users.UpsertMany(members);
var channel = this._client.Channel("messaging", System.Guid.NewGuid().ToString());
await channel.Create(user1.ID);
await channel.AddMembers(new string[] { user1.ID, user2.ID });

var resp = await channel.AssignRoles(new AssignRoleRequest
{
AssignRoles = new List<RoleAssignment>
{
new RoleAssignment { UserId = user1.ID, ChannelRole = Role.ChannelModerator },
new RoleAssignment { UserId = user2.ID, ChannelRole = Role.ChannelModerator }
},
Message = new MessageInput { Text = "Test message", User = user1 }
});

Assert.That(resp, Is.Not.Null);
}
}
}
143 changes: 143 additions & 0 deletions src/stream-chat-net-test/PermissionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using NUnit.Framework;
using StreamChat;

namespace StreamChatTests
{
/// <summary>
/// Even if we create/update/delete a new role, we still need
/// to wait a few seconds for the changes to propagate in the backend.
/// For this reason, we wrote just a single test to test all functionalites at once.
/// </summary>
public class PermissionTests
{
private readonly IClient _client = TestClientFactory.GetClient();
private const string TestPermissionDescription = "Test Permission";

[OneTimeSetUp]
[OneTimeTearDown]
public async Task CleanupAsync()
{
await DeleteCustomRolesAsync();
await DeleteCustomPermissonsAsync();
}

private async Task DeleteCustomRolesAsync()
{
var roleResp = await _client.Permissions.ListRoles();
foreach (var role in roleResp.Roles.Where(x => x.Custom))
{
try
{
await _client.Permissions.DeleteRole(role.Name);
}
catch
{
// It throws 'role does not exist' exception which
// is obviously nonsense. So let's ignore it.
}
}
}

private async Task DeleteCustomPermissonsAsync()
{
var permResp = await _client.Permissions.ListPermissions();
foreach (var perm in permResp.Permissions.Where(x => x.Description == TestPermissionDescription))
{
try
{
await _client.Permissions.DeletePermission(perm.Id);
}
catch
{
// It throws 'not found' exception which
// is obviously nonsense. So let's ignore it.
}
}
}

[Test]
public async Task TestRolesEnd2endAsync()
{
// Test create
var roleResp = await _client.Permissions.CreateRole(Guid.NewGuid().ToString());

Assert.That(roleResp.Role.Name, Is.Not.Null);
Assert.That(roleResp.Role.Custom, Is.True);
Assert.That(roleResp.Role.CreatedAt, Is.EqualTo(DateTime.UtcNow).Within(TimeSpan.FromSeconds(10)));
Assert.That(roleResp.Role.UpdatedAt, Is.EqualTo(DateTime.UtcNow).Within(TimeSpan.FromSeconds(10)));

// Test list
var rolesResp = await _client.Permissions.ListRoles();
Assert.That(rolesResp.Roles, Is.Not.Empty);

// Test delete
try
{
await _client.Permissions.DeleteRole(roleResp.Role.Name);
}
catch (StreamChatException ex)
{
if (ex.Message.Contains("does not exist"))
{
// Unfortounatly, the backend is too slow to propagate the role creation
// so this error message is expected. Facepalm.
return;
}

throw;
}
}

[Test]
public async Task TestPermissionsEnd2endAsync()
{
var permission = new Permission
{
Id = Guid.NewGuid().ToString(),
Name = Guid.NewGuid().ToString(),
Description = TestPermissionDescription,
Action = "DeleteChannel",
Condition = new Dictionary<string, object>
{
{"$subject.magic_custom_field", new Dictionary<string, string> {{"$eq", "true"}}}
}
};

// Test create
await _client.Permissions.CreatePermission(permission);

// Test Get
var permResponse = await _client.Permissions.GetPermission("upload-attachment-owner");
Assert.That(permResponse.Permission.Id, Is.Not.Null);
Assert.That(permResponse.Permission.Name, Is.Not.Null);
Assert.That(permResponse.Permission.Description, Is.Not.Null);
Assert.That(permResponse.Permission.Action, Is.Not.Null);
Assert.That(permResponse.Permission.Condition, Is.Not.Null);

// Test list
var listResp = await _client.Permissions.ListPermissions();
Assert.That(listResp.Permissions, Is.Not.Empty);

// Test delete
try
{
await _client.Permissions.DeletePermission(permission.Id);
}
catch (StreamChatException ex)
{
if (ex.Message.Contains("not found"))
{
// Unfortounatly, the backend is too slow to propagate the permission creation
// so this error message is expected. Facepalm.
return;
}

throw;
}
}
}
}
19 changes: 8 additions & 11 deletions src/stream-chat-net-test/Utils.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,15 @@ public static class TestClientFactory
Timeout = 10000
});

public static StreamChat.IClient GetClient()
{
return _defaultClient;
}
public static StreamChat.IClient GetClient() => _defaultClient;

public static StreamChat.IClient GetClient(HttpClient httpClient)
{
return new StreamChat.Client(_apiKey, _apiSecret, httpClient,
new StreamChat.ClientOptions
{
Timeout = 10000
});
}
=> new StreamChat.Client(
_apiKey,
_apiSecret,
httpClient,
new StreamChat.ClientOptions{ Timeout = 10000 }
);

}
}
13 changes: 13 additions & 0 deletions src/stream-chat-net/ApiResponse.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace StreamChat
{
/// <summary>
/// ApiResponse contains fields that every Stream response includes.
/// </summary>
public class ApiResponse
{
/// <summary>
/// Duration of the request in human-readable format.
/// </summary>
public string Duration { get; set; }
}
}
13 changes: 13 additions & 0 deletions src/stream-chat-net/Channel.Members.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,18 @@ public async Task RemoveMembers(IEnumerable<string> userIDs, MessageInput msg =
throw StreamChatException.FromResponse(response);
}

public async Task<UpdateChannelResponse> AssignRoles(AssignRoleRequest roleRequest)
{
var request = this._client.BuildAppRequest(this.Endpoint, HttpMethod.POST);
request.SetJsonBody(roleRequest.ToJObject().ToString());

var response = await this._client.MakeRequest(request);
if (response.StatusCode != System.Net.HttpStatusCode.Created)
throw StreamChatException.FromResponse(response);

var respObj = JObject.Parse(response.Content);
return UpdateChannelResponse.FromJObject(respObj);
}

}
}
6 changes: 4 additions & 2 deletions src/stream-chat-net/ChannelType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ namespace StreamChat
public class ChannelType : ChannelConfigWithInfo
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "permissions")]
public List<Permission> Permissions { get; set; }
[Obsolete("Use V2 Permissions APIs instead. " +
"See https://getstream.io/chat/docs/dotnet-csharp/migrating_from_legacy/?language=csharp")]
public List<ChannelTypePermission> Permissions { get; set; }
}

public class ChannelTypeInput : ChannelType
Expand All @@ -26,7 +28,7 @@ public class ChannelTypeInput : ChannelType
public class ChannelTypeOutput : ChannelType
{
[JsonProperty(NullValueHandling = NullValueHandling.Ignore, PropertyName = "commands")]
public new List<string> Commands { get; set; }
public new List<string> Commands { get; set; }
}

public class ChannelTypeInfo : ChannelType
Expand Down
14 changes: 7 additions & 7 deletions src/stream-chat-net/Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,16 @@ private Client(ClientOptions opts, HttpClient httpClient, string apiKey, string
};
_token = this.GenerateJwt(payload);
var assemblyVersion = typeof(Client).GetTypeInfo().Assembly.GetName().Version;

Version = string.Join(".", assemblyVersion.Major, assemblyVersion.Minor, assemblyVersion.Build);
Users = new Users(this);
Permissions = new PermissionClient(this);
}

public IUsers Users
{
get
{
return new Users(this);
}
}
public IUsers Users { get; }

public IPermissions Permissions { get; }


public string CreateToken(string userId, DateTime? expiration = null, DateTime? issuedAt = null)
{
Expand Down
1 change: 1 addition & 0 deletions src/stream-chat-net/IChannel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public interface IChannel

Task BanUser(string targetID, string id, string reason, int timeoutMinutes = 0);
Task UnbanUser(string targetID);
Task<UpdateChannelResponse> AssignRoles(AssignRoleRequest roleRequest);

Task Hide(string userID, bool clearHistory = false);
Task Show(string userID);
Expand Down
1 change: 1 addition & 0 deletions src/stream-chat-net/IClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace StreamChat
public interface IClient
{
IUsers Users { get; }
IPermissions Permissions { get; }

string CreateToken(string userId, DateTime? expiration = null, DateTime? issuedAt = null);

Expand Down
Loading

0 comments on commit 26fb0f5

Please sign in to comment.