Resource level authorization with Signalr - signalr

Should Signalr groups be created with resource level IDs? For example dispatchedTrucks-123, and dispatchedTrucks-456? So that way when someone tries to connected to a group, I can check to see if they have access?
Let's say there are two companies: Company1 (Id 123) and Company2 (Id 456), each company has employees, and each company dispatches their trucks.
If I'm creating a Signalr hub, is it best practice to create a dispatchedTrucks group per companyId, then check to see if that people has access to that company before adding them to the group?
public async Task AddToGroup(string groupName)
{
var companyId = Context.QueryString["companyId"];
if (HasAccessToCompany(companyId)){
await Groups.AddToGroupAsync(Context.ConnectionId, $`{groupName}-{companyId}`);
} else {
// What's the Signalr equivalent of a 403?
}
}
So later in the application I can broadcast to an entire group, so the only people who receive the message is those who have access to that companyId.
var companyId = Context.QueryString["companyId"];
await Clients.Group($`{groupName}-{companyId}`).addDispatchedTruck(name, message);
The RESTful equivalent of what I'm trying to accomplish is:
UserA works for Company1 (Id 123).
UserB works for Company2 (Id 456).
UserA is only authorized to make a GET request to /company/123/dispatched-trucks
UserB is only authorized to make a GET request to /company/456/dispatched-trucks
If UserA tries to call Company2's resources, a 403 is returned.
If UserB tries to call Company1's resources, a 403 is returned.

Related

How Do I Store Extra Private Information Related To A State?

I have a simple flow that creates a state between a buyer and seller and obviously each side can see everything on the state.
However, I now have a requirement that the buyer wants to store the user that processed the transaction for auditing and reporting purposes.
The user in this case is not a node or an account but a user that has logged in to the application and been authorised via Active Directory.
I could just add the user name to the state as a String but that would expose private data to the seller.
An alternative would be to obfuscate the name in some way but I would rather store the information in a separate table outside the state and only in the buyers vault.
How do I do this and is there a sample that demonstrates it?
You can create a second output state, which is used in the same transaction, but has only the token issuer as participant. Then of course, it is up to you to make the link between the "issued state" and the "recorder state", it depends on what you will store inside the latter.
Let's make an example of a fungible token issuance from Node1 to Node2. You could create a "issuance recorder state" that aims at recording something on Node1's vault only, like so (note the list of participants):
// the "recorder" state visible only in Node1's vault
#BelongsToContract(IssuanceRecordContract::class)
class IssuanceRecord(
val holder: AbstractParty,
val amount: Amount<IssuedTokenType>
) : ContractState {
override val participants: List<AbstractParty>
get() = listOf(amount.token.issuer)
}
and then you could pass it to the same TransactionBuilder that you are using to issue the fungible token (which instead has both parties in the list of participants), like so:
// This is from the Issuanflow launched from Node1
#Suspendable
override fun call(): String {
...
...
// Create the FungibleToken (issuedToken is an IssuedTokenType created before)
val tokenCoin = FungibleToken(Amount(volume.toLong(), issuedToken), holderAnonymousParty)
// create the "recorder" output state visible only to Node1
val issuanceRecord = IssuanceRecord(holderAnonymousParty, tokenCoin.amount)
// create the Transaction Builder passing the "recorder" output state
val transactionBuilder = TransactionBuilder(notary).apply {
addOutputState(issuanceRecord, IssuanceRecordContract.ID)
addCommand(Command(IssuanceRecordContract.Commands.Issue(), ourIdentity.owningKey))
}
// Issue the token passing the transactionBuilder and the fungible token
addIssueTokens(transactionBuilder, tokenCoin)
// collect signatures
// verify transaction
// FinalityFlow (fundamental to make this work in Node1)
}
This way, I think, the recorder states will be atomically stored in Node1's vault. If something happens, the transaction will be not successful for both output states.

MS-Graph read tasks with admin consent

I am trying to read the Planner task for the users of my tenant. So I configured admin consent for "Tasks.Read", "Tasks.Read.Shared", "Groups.Read.All", "Groups.ReadWrite.All" for the app that is doing that.
Like mentioned here: https://learn.microsoft.com/de-de/graph/api/planneruser-list-tasks?view=graph-rest-1.0&tabs=http
I desined my code to get a token like mentioned here: https://briantjackett.com/2018/12/13/introduction-to-calling-microsoft-graph-from-a-c-net-core-application/
I get a token back and it is valid. (Checked with baerer token check tool.)
I thought that I could access the tasks from the Graph API like '/v1.0/users/{userId}/planner/tasks' but I get HTTP 401 back.
Can anyone give me the missing link? Thanks a lot.
_appId = configuration.GetValue<string>("AppId");
_tenantId = configuration.GetValue<string>("TenantId");
_clientSecret = configuration.GetValue<string>("ClientSecret");
_clientApplication = ConfidentialClientApplicationBuilder
.Create(_appId)
.WithTenantId(_tenantId)
.WithClientSecret(_clientSecret)
.Build();
var graphClient = GraphClientFactory.Create(new DelegateAuthenticationProvider(Authenticate));
var result = await graphClient.GetAsync($"/v1.0/users/{userId}/planner/tasks")
public async Task<string> GetTokenAsync()
{
AuthenticationResult authResult = await _clientApplication.AcquireTokenForClient(_scopes)
.ExecuteAsync();
return authResult.AccessToken;
}
private async Task Authenticate(HttpRequestMessage request)
{
var token = await GetTokenAsync();
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", token);
}
Reading tasks for other users is currently not allowed. A user can only read their assigned tasks. As an alternative, you can read the tasks in specific Plans, and sort out the users from that data, if you want to collect assignments from a set of Plans. You can provide feedback on this behavior in Planner UserVoice with the description of what you are trying to accomplish.
Additionally, application permissions are supported now, if that works for your scenario.
/v1.0/users/{userId}/planner/tasks is for getting tasks via getting a user, and you will need User permissions (User.Read.All) to get tasks via that call.
(Also Do you really need Groups.ReadWrite.All? Are you making changes to groups? -- it's not in your original description)

DocumentDB Permissions on Collections

In my design I want to use DocumentDB as a back-end for my multi tenant SAAS application.
As the data per tenant can be > 10gb I want to use a collection per tenant model.
The requirement is how can I ensure data isolation in my application. Tenant 1 should not be able to see tenant 2 data in code and on azure portal. How can I set permissions on collections for the same?
I want to use a collection per tenant model
You could try to create DocumentDB users for each tenant and associate the permissions with the user to control user access to collection/documents.
string documentCollectionlink = UriFactory.CreateDocumentCollectionUri("{database id}", "{collection id}").ToString();
string databaseLink = UriFactory.CreateDatabaseUri("{database id}").ToString();
Permission docPermission = new Permission
{
PermissionMode = PermissionMode.All,
ResourceLink = documentCollectionlink,
Id = "tenant1perm"
};
await client.CreateUserAsync(databaseLink, new User { Id = "tuser" });
docPermission = await client.CreatePermissionAsync(UriFactory.CreateUserUri("{database id}", "tuser"), docPermission);
and then you could read permissions for DocumentDB user and get access token based on the tenant, and create a new instance of the DocumentClient with the token to operate the resource.
var qclient = new DocumentClient(new Uri(EndpointUri), token);

Filter out some entries for some users in SignalR

I'm trying to build what I call a "data feed" that uses SignalR but the key to what I want to do is that I want to filter out some data for some users based on their preferences.
So, think of a chat room where each entry has some tags e.g. fitness, baseball, etc.
If user A has baseball in his subscribed tags, the next entry that has "baseball" tag in it will be visible to User A. However, user B should not see that entry if he doesn't have "baseball" tag in his preferences.
In other words, a chat room where users do NOT see every entry but only the ones that match their tag subscriptions.
Is this possible with SignalR?
What you are looking for is the SignalR Groups. Each user can join/leave different groups based on their preferences:
public Task JoinGroup(string tagName)
{
return Groups.Add(Context.ConnectionId, tagName);
}
public Task LeaveGroup(string tagName)
{
return Groups.Remove(Context.ConnectionId, tagName);
}
public Task SendMessage(string message, string tagName)
{
Clients.Group(tagName).addChatMessage(message);
}

Create chat group in SignalR

How can I create a chat group in SignalR? I tried to find some examples and they weren't helpful. Any help would be appreciated. Here is what I've come up with so far:
public void CreateGroup(string currentUserId, string toConnectTo)
{
string strGroupName = GetUniqueGroupName(currentUserId, toConnectTo);
string connectionId_To = OnlineUser.userObj.Where(item => item.userId == toConnectTo).Select(item => item.connectionId).SingleOrDefault();
if (!string.IsNullOrEmpty(connectionId_To))
{
Groups.Add(Context.ConnectionId, strGroupName);
Groups.Add(connectionId_To, strGroupName);
Clients.Caller.setChatWindow(strGroupName, toConnectTo);
}
}
It's not such easy to create a user friendly chat group that I'm currently working on.
I use sql database, user can register, login, logout, close browser or phone app without logout.
user can create own group, add members to group, delete member from group.
when user send message, call signalr Server method and store message in sql, then Server send message to all users in the same group. if some users are offline, they read message from sql when online again.
the input for the message can be a 'contenteditable div' so that user can add images and formatted text to the message.
something like that!

Resources