how to register SQL Table dependency for non dbo user - c#

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.

Related

CallerId of OrganizationServiceProxy

I am making a external service which will create a record in Order entity of Dynamics 365 with the default field(e.g: created by and some default fields) will be named after the user who wants to create.
For that I tried using the CallerId property on the OrganizationServiceProxy class. I am setting the CallerId property by the user of CRM who is actually wanting to create a record. But I’m only able to create record if the user of crm has System Administrator role.
Some block of code is added for better understanding:
public void Get(Guid userId)
{
var proxy = new OrganizationServiceProxy(new Uri(c.ServiceUri), null, crmCredentials, null);
proxy.EnableProxyTypes();
var context = new OrganizationContext(proxy);
// now setting caller id
proxy.CallerId = userId;
// generating order entity
var t = new SalesOrder();
t.Name = "Demo";
.....
...
.
context.AddObject(t);
context.SaveChanges(); // getting exceptions for normal user on save changes
}
Now my question is how to overcome the exception if the user of crm is not privileged with System Administrator role.
Verify if any of the security role assigned to that “normal user” has create privilege granted on minimum user level (orange pie) for “Order” entity (sales order) under “Sales” tab. I guess not.
Give that privilege & verify the same code execution.
Your code works fine as long as the user that you are setting as CallerId has the Sales Manager Security Role. The user you are logging in with must also have at least a Sales Manager Security Role and the Act on Behalf of Another User privilege that can be found under Business Management tab.

Prevent SELECT on a database during a transaction

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

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 implement a password change functionality with Oracle and a .Net client?

I'm using Oracle users to authenticate username and password for a .Net application. Right now I'm working on the password change function. The database has a custom password validation, so if you try to change a users password and you provide an invalid password, Oracle returns multiple errors.
The first error is always "ORA-28003: password verification for the specified password failed", and then it rises one error for each failed validation. This is shown correctly when I try to change a user's password from the Toad client.
However, when I do this from my application, the OracleException that is raised only returns the first error, and therefore I'm not able to show the user what's invalid about the new password he provided, which is a requirement for the application. So how should I aproach this?
For starters, don't use the OpenWithNewPassword method. Not only does it have known issues with with various versions of ODP.net and the DB, but it forces you to have two different branches of code when you only need one - IIRC it doesn't work if the user's password has already expired.
Instead the basic logic works like this:
Make sure you can authenticate with the user's old account and password
If you're successful, close that connection and open a separate account that has no access other than exec privs on a ChangePassword stored procedure.
Here's the code:
protected void BtnChangePassword_Click(object sender, EventArgs e)
{
String connectionStringFormat = "Data Source={0};User Id={1};Password={2};pooling=false;";
if (Page.IsValid)
{
Boolean hasHasError = false;
String connectionString = String.Format(
connectionStringFormat,
IptDatabase.Text,
IptUserName.Text,
IptOldPassword.Text);
OracleCommand cmd = new OracleCommand();
using (cmd.Connection = new OracleConnection(connectionString))
{
try
{
cmd.Connection.Open();
}
catch (OracleException ex)
{
//allow to continue if the password is simply expired, otherwise just show the message
if (ex.Number != 28001)
{
ShowErrorMessage(ex.Message);
hasHasError = true;
}
}
if (!hasHasError)
{
//successful authentication, open as password change account
cmd.Connection.Close();
cmd.Connection.ConnectionString = ConfigurationManager.ConnectionStrings[IptDatabase.Text].ConnectionString;
cmd.Connection.Open();
cmd.CommandText = "SysChangePassword";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("username", IptUserName.Text);
cmd.Parameters.Add("newpassword", IptPassword.Text);
try
{
cmd.ExecuteNonQuery();
ShowInfoMessage("Password Changed");
}
catch (OracleException ex)
{
ShowErrorMessage(ex.Message);
}
}
}
}
In it's simplest form, the proc executes 'alter user identified by and would be similar to the one documented here: http://www.adp-gmbh.ch/ora/plsql/change_password.html. However the dbms_output lines don't do you much good so you could throw custom exceptions instead:
create or replace procedure SysChangePassword(
pUserName in varchar2,
pPassWord in Varchar2) as
begin
-- Check for system users here and reject
if upper(pUserName) in ('SYS','SYSTEM') then
raise_application_error(-20012, 'not allowed');
else
execute immediate 'alter user '||pUserName||' identified by ' ||
pPassWord;
end if;
exception --this isn't necessary if you'd rather examine and handle the specific exceptions on the .net side
when others then
raise_application_error(-20012, sqlerrm);
end;
/
The schema that owns this procedure needs 'alter any user' privleges. For safety's sake, your app should connect as a separate user that only has execute privs on this proc. Rather
Why don't you wrap the logic in a stored procedure? It would call the Oracle password validation functions, then you parse the results as needed, and return whatever messages you want back to the .net client?
Why don't you use a Regular Expression Validator control if writing for ASP.NET, Or Regex.IsMatch(...) Function if writing desktop applications? Why you have to go to the server first to get the error generated. you have strong password policy, and you can restrict the user on client side.

Using SMO.Agent to retrieve SQL job execution status - security issue

I've got a C# program that fires off SQL Server Agent jobs using the SQL Server Management Objects (SMO) interfaces. It looks something like:
Server ssis_server = new Server(
new ServerConnection(SERVER_NAME, SERVER_USERNAME, SERVER_PASSWORD)
);
var agent = ssis_server.JobServer;
var ssis_job = agent.Jobs[job_name];
var current_status = ssis_job.CurrentRunStatus;
if (current_status == JobExecutionStatus.Idle)
{
ssis_job.Start();
OnSuccess("Job started: " + job_name);
}
else
{
OnError("Job is already running or is not ready.");
}
I'm using SQL Server Authentication at this point to simplfy things whilst I work this out.
Now, my problem is that unless the SERVER_USERNAME is part of the 'sysadmin' dbo role, ssis_job.CurrentRunStatus is always 'Idle' - even when I know the job is running. It doesn't error out, just always reports idle.
If the user is an administrator, then the status is returned as expected.
Role membership you say?
Well, I added the SERVER_USERNAME SQL Server login to the msdb Role SQLAgentOperatorRole, that didn't seem to help.
The job's owner is a system administrator account - if that's the issue I'm not sure how to work around it.
Any ideas?
You need to refresh the job by calling the Refresh() method on ssis_job before checking the status, then you will get the correct information.

Categories