Prevent SELECT on a database during a transaction - c#

I am building a Web API on which I apply some custom security mechanisms. In order to check if an operation is authorized I need to save its changes in the database within a transaction and do a commit or a rollback after.
Here is a simplified version of what I am doing:
public IHttpActionResult Post(Account account)
{
using (DbContextTransaction transaction = Context.Database.BeginTransaction())
{
Context.accounts.Add(account);
Context.SaveChanges();
// This will do one or several SELECTs on the database to do the verification
RollbackIfForbidden(transaction, account);
transaction.Commit();
}
return Ok();
}
What I need is to set an IsolationLevel on my transaction that locks the table (or the database) until the transaction is committed or rollbacked. This lock should delay any query done on the database while it is locked except for those that are sent by the method RollbackIfForbidden.
This would allow to prevent people to get erroneous data if they query the database between Context.SaveChanges(); and transaction.Commit(); but still accept any query done inside RollbackIfForbidden(transaction, account);.
Edit:
User story
The user sends a POST query to create an Account entity.
The service begins a transaction and store the account in the database.
The database can apply some modifications on the entity (via triggers or constraints).
The service query the new account with the changes made by the database.
The service checks that the current user has the right to create this specific account (based on the value that the user filled and that the database completed)
The service do a commit if the user is permitted, or do a rollback if he is forbidden.
The service returns the status of the operation (success or error).
My problem is that between the steps 2 and 6, another user may do a query on the database and get the created account while it may be removed in the step 6.
So I need to lock the database in the meantime but I also need to query the database in step 4 to get the new values of my entity.

You can use sp_getapplock to set your locking for transaction in sql server
https://msdn.microsoft.com/en-us/library/ms189823.aspx

Related

Realm sync permissions for flexibly-named partitions based on user id

I'm new to Realm Sync (and Realm). I'm trying to convert a REST / SQL Server system to Realm Sync (to avoid having to write my own local-device caching code).
I got a simple configuration working, with a single API-key user and the null partition, read and write permissions just set to true.
But for my more complex application, I want smaller sub-partitions to reduce the amount of data that needs to be cached on local devices, and I want the sub-partitions to be able to be created dynamically by the client. Ideally, I would like to allow an API-key user to connect to any partition whose name starts with their user id (or some other known string, e.g. the profile name). But I can't find a way to get a "starts with" condition into the permissions.
My best attempt was to try setting Read and Write sync permissions to:
{
"%%partition": {
"$regex": "^%%user.id"
}
}
but my client just fails to connect, saying Permission denied (BIND, REFRESH). (Yes, I tried using "$regex": /^%%user.id/ but the Realm UI rejected that syntax.) The Realm Sync log says "user does not have permission to sync on partition (ProtocolErrorCode=206)".
As you can see in the log image, the partition name was equal to the user id for this test.
Is what I'm trying to do possible? If so, how do I set up the Sync Permissions to make it work?
This can be done using a function. If, like me, you're new to Realm Sync and not fluent in Javascript, don't worry - it turns out to be not too hard to do, after all. (Thanks Jay for encouraging me to try it!)
I followed the instructions on the Define a Function page to create my userCanAccessPartition function like this:
exports = function(partition){
return partition.startsWith(context.user.id);
};
Then I set my sync permissions to:
{
"%%true": {
"%function": {
"name": "userCanAccessPartition",
"arguments": ["%%partition"]
}
}
}

how to register SQL Table dependency for non dbo user

We are trying to enable SQLTableDependency from .Net code using Non-DBO SQL user, Currently, it's throwing a permission exception "The specified schema name "dbo" either does not exist or you do not have permission to use it ".
var connectionString = dataAccessManage.ConnectionStringToBroker;
_tableDependency = new SqlTableDependency<Plan>(connectionString, schemaName : "",executeUserPermissionCheck: false); ;
try
{
_tableDependency.OnChanged += Plan_OnChanged;
_tableDependency.Start();
Console.WriteLine("Waiting for receiving notifications...");
}
catch (Exception ex)
{
LogsWritter.SendErrorToText(ex, errorLogConfiguration);
_tableDependency.Stop();
throw;
}
We did some troubleshooting and found if we grand below permission for NON-DBO users it will work fine.
Permission -
ALTER
CONNECT
CONTROL
CREATE CONTRACT
CREATE MESSAGE TYPE
CREATE PROCEDURE
CREATE QUEUE
CREATE SERVICE
EXECUTE
SELECT
SUBSCRIBE QUERY NOTIFICATIONS
VIEW DATABASE STATE
VIEW DEFINITION
The problem is these permission statements will not pass our security review. We cannot authorize db_owner permissions, and cannot grant ALTER, CONTROL, EXECUTE, and SELECT to the entire database (which is basically db_owner permissions).
So need to know the solution for enabling SQLTableDependency for limited permission user, Which passes our security review.

ASP .NET Core using InMemory Cache per user

I have a system where at some point, the user will be locked to a single page. In this situation his account his locked and he cannot be redirected to any other page and this is after authentication.
The verification is done using Page Filters accessing database. To improve performance I have used memory cache.
However, the result wasn't as expected because once the cache is used for a single user it will affect all the others.
As far as i know, you can separate caching using tag helpers per user but I have no idea if this is possible using code
public async Task<IActionResult> Iniciar(int paragemId, string paragem)
{
var registoId = Convert.ToInt32(User.GetRegistoId());
if (await _paragemService.IsParagemOnGoingAsync(registoId))
{
return new JsonResult(new { started = false, message = "Já existe uma paragem a decorrer..." });
}
else
{
await _paragemService.RegistarInicioParagemAsync(paragemId, paragem, registoId);
_registoService.UpdateParagem(new ProducaoRegisto(registoId)
{
IsParado = true
});
await _registoService.SaveChangesAsync();
_cache.Set(CustomCacheEntries.RecordIsParado, true, DateTimeOffset.Now.AddHours(8));
return new JsonResult(new { started = true, message = "Paragem Iniciada." });
}
}
here i only check first if the user account is blocked in the database first without checking cache first and then create the cache entry.
Every user will be locked because of this.
So my point is... Is there a way to achieve this like tag helpers?
The CacheTagHelper is different than cache in general. It works via the request and therefore can vary on things like headers or cookie values. Just using MemoryCache or IDistributedCache directly is low-level; you're just adding values for keys directly, so there's nothing here to "vary" on.
That said, you can compose your key using something like the authenticated user's id, which would then give each user a unique entry in the cache, i.e. something like:
var cacheKey = $"myawesomecachekey-{User.FindFirstValue(ClaimTypes.NameIdentifier)}";
Short of that, you should use session storage, which is automatically unique to the user, because it's per session.
There are several alternatives to the cache. For details please see this link that describes them in greater detail.
Session State
An alternative would be to store the value in session state. This way, the session of one user does not interfere with the ones of others.
However, there are some downsides of this approach. If the session state is kept in memory, you cannot run your application in a server farm because one server does not know of the others session memory. So you would need to save the session state in a cache (REDIS?) or a database.
In addition, as session memory is stored in the server users cannot change it and avoid the redirection that you try to implement. The downside is that this reduces the amount of users that your server can handle because the server needs to have a specific amount of memory per user.
Cookies
You can send a cookie to the client and check for this cookie when the next request arrives at your server. The downside of this approach is that the user can delete the cookie. If the only consequence of a missing cookie is a request to the database, this is neglectable.
You can use session cookies that are discarded by the server when the session expires.
General
Another hint is that you need to clear the state memory when a user signs out so that with the next sign in, the state is correctly set up for the new user.

SqlDependency not updating cache

I'm on a project using .NET 4.5, MVC, EF 6
I had naively implemented a caching system using the HttpRuntime cache and needed to invalidate the data I cache on updates to that data; except I forgot to take into account that our production server is published to a load balanced set of two servers... :|
So on production, after the data was updated, the app would sometimes serve the right data, and sometimes the old data depending on which server the request was hitting. Bad news bears.
So I decided to define a dependency on the SQL table AcademicTerms which is where my data is coming from. But I did something wrong, and I'm not sure what.
SQL that I ran to set up the permissions after enabling the Service Broker
EXEC sp_addrole 'sql_dependency_role'
GRANT CREATE PROCEDURE to sql_dependency_role
GRANT CREATE QUEUE to sql_dependency_role
GRANT CREATE SERVICE to sql_dependency_role
GRANT REFERENCES on
CONTRACT::[http://schemas.microsoft.com/SQL/Notifications/PostQueryNotification]
to sql_dependency_role
GRANT VIEW DEFINITION TO sql_dependency_role
GRANT SELECT to sql_dependency_role
GRANT SUBSCRIBE QUERY NOTIFICATIONS TO sql_dependency_role
GRANT RECEIVE ON QueryNotificationErrorsQueue TO sql_dependency_role
EXEC sp_addrolemember 'sql_dependency_role', 'MY_ASPNET_APP_USERNAME'
My implementation of inserting new data after fetching and thus setting up the SqlDependency (hopefully less naive!):
private void insertIntoCache(
AcademicTermLockingInfo newItem,
string itemKey,
Guid termID) {
var dbContextConnection = db.Database.Connection;
var connectionString = dbContextConnection.ConnectionString;
// important step otherwise it won't work
SqlDependency.Start(connectionString);
CacheItemPolicy policy = new CacheItemPolicy {
AbsoluteExpiration = DateTime.UtcNow.AddMonths(6)
};
CacheItem item = new CacheItem(itemKey, newItem);
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
// command which will be used to notify updates - probably want to parametrize this
using (SqlCommand command =
new SqlCommand(
String.Format("SELECT Name, LockDate FROM dbo.AcademicTerms WHERE ID = '{0}'",
termID),
connection)) {
SqlDependency dependency = new SqlDependency(command);
SqlChangeMonitor monitor = new SqlChangeMonitor(dependency);
policy.ChangeMonitors.Add(monitor);
MemoryCache.Default.Set(item, policy);
// execute once otherwise dependency not registered
command.ExecuteNonQuery();
}
}
}
Any help would be very much appreciated!
Things I've done:
Created two new users in SQL Server, net and sanba
Added every NT* login and the sa login to the net user, added net to the sql_dependency_role
Ran grant alter on schema::sql_dependency_role to net and grant alter on schema::dbo to net
Check that my local SQL Server's Broker Enabled option is True under Service Broker
Tried the web cache and the Memory Cache interchangeably (probably wouldn't change anything)
Tried making the sql command string have a fully qualified name DevUMS.dbo.AcademicTerms and dbo.AcademicTerms
I queried the sys.dm_qn_subscriptions and saw I had one subscription, good!
I queried DevUMS.sys.transmission_queue and found an excpetion!
An exception occurred while enqueueing a message in the target
queue. Error: 15517, State: 1. Cannot execute as the database
principal because the principal "dbo" does not exist, this type of
principal cannot be impersonated, or you do not have permission.
I found this SO post with the same error
The secret sauce I was missing was alter authorization on database::DevUMS to [sa]; which I found on the linked SO post's answer.
There are a number of other steps, like adding a Role to use the appropriate login, but honestly, I'm really unsure as to whether or not those are actually necessary.
I'm going to publish a little later on today, and then I'll try to do the minimal amount of steps and document that here. I found the documentation in the wild to be very scattered and poor, so I hope to have this answer be a definitive place to refer to in the future

How to Roll back web service call

Suppose I call a webservice to save some data to their local DB. After I get response from the webservice I check the response for following situation:
1- If failed to insert, I can easily roll back my entire code block using TransactionScope (In C#).
2- If it was successful I try to save these state (successful state) to my database. But before save the state my database server crashed! So I need to roll back web service call!
The code sample is like these:
//Calling database
var response = client.InsertNewItem(data);
if(response.IsSuccess)
{
myDb.SaveSucessfulState();
//Commit transaction
scope.compelte();
}
else
{
//Do roll back stuff.
}
You can look into WCF and transactions
http://msdn.microsoft.com/en-us/library/ff384250.aspx

Categories