Is there any way of changing a user permissions for table in Access database using C#? I have a problem to read and insert into table which has denied read/insert rights for current user. When I change manually permissions works, but I want to change it programatically by C#. I've been tried this SQL command:
GRANT SELECT ON TABLE myTable TO Admin
but it's seems that I don't have rights for that action. This how it looks in Access.
Edit:
To call #rybo103 method, I've used this code, but I'm getting an error.
DAO.DBEngine engine = new DAO.DBEngine();
string path = #"C:\Database.mdb";
DAO.Database db = engine.OpenDatabase(path, false, false, "");
List<DAO.PermissionEnum> flags = new List<PermissionEnum>();
flags.Add(DAO.PermissionEnum.dbSecFullAccess);
AddPermissions(db, "ECR", "Admins", flags);
Error:
Cannot open the Microsoft Office Access database engine workgroup information file.
In Access you will need to use DAO to make these sort of schema changes.
https://msdn.microsoft.com/en-us/library/office/ff835373.aspx
void AddPermissions(DAO.Database db, string tableName, string groupName, IList<DAO.PermissionEnum> addFlags)
{
var container = db.DaoDB.Containers["Tables"];
var document = container.Documents[tableName];
document.UserName = groupName;
int permissions = document.Permissions;
foreach (var flag in addFlags)
{
permissions = permissions | (int)flag;
}
document.Permissions = permissions;
}
#HansUp sugestons helped. Finnaly, I added Jet OLEDB:System Database property to my OleDb connection string and it works. Even it works without GRANT command.
Related
We have several environments:
Development
Azure
On Prem
The Crystal Report pulls data from a SQL server, but the database is different depending on the environment.
There is a config file in the project called connectionStrings.config and the connection string that we use for the website is stored there. The contents of the file looks like this:
<connectionStrings>
<add name="dataModel" connectionString="data source=...;initial catalog=...;user id=...;password=...;MultipleActiveResultSets=True;App=EntityFramework" providerName="System.Data.SqlClient" />
</connectionStrings>
To load the report I setup the following class:
public class CrystalReportUtil
{
static string ReportsDirectory = ConfigurationManager.AppSettings["ReportsDirectory"];
private static string ConnectionString = ConfigurationManager.ConnectionStrings["dataModel"].ConnectionString;
public static ReportDocument GetReport(string reportName)
{
var report = new ReportDocument();
var fileDirectory = $"{Utilities.GetProjectRoot()}/Reports";
if (System.Web.HttpContext.Current != null)
{
fileDirectory = System.Web.HttpContext.Current.Server.MapPath(ReportsDirectory);
}
string file = Path.Combine(fileDirectory, $"{reportName}.rpt");
if (!File.Exists(file))
{
throw new System.Exception($"Unable to find report file: {file}");
}
report.Load(file);
var builder = new SqlConnectionStringBuilder(ConnectionString);
var dataSource = builder.DataSource;
var user = builder.UserID;
var password = builder.Password;
var database = builder.InitialCatalog;
RecursivelyRemapConnection(report, user, password, dataSource, database);
report.VerifyDatabase();
return report;
}
private static void RecursivelyRemapConnection(ReportDocument report, string username, string password, string server, string database)
{
foreach (IConnectionInfo connection in report.DataSourceConnections)
{
connection.SetLogon(username, password);
connection.SetConnection(server, database, false);
}
report.SetDatabaseLogon(username, password);
foreach (Table table in report.Database.Tables)
{
table.LogOnInfo.ConnectionInfo.ServerName = server;
table.LogOnInfo.ConnectionInfo.DatabaseName = database;
table.LogOnInfo.ConnectionInfo.UserID = username;
table.LogOnInfo.ConnectionInfo.Password = password;
table.LogOnInfo.ConnectionInfo.IntegratedSecurity = false;
}
if (!report.IsSubreport)
{
foreach (ReportDocument subreport in report.Subreports)
{
RecursivelyRemapConnection(subreport, username, password, server, database);
}
}
}
}
The issue is that in my development environment, it is working just fine whereas in the other environments I get the following exception:
Log on failed. No error.
at CrystalDecisions.ReportAppServer.ClientDoc.ReportClientDocumentClass.VerifyDatabase()
at CrystalDecisions.ReportAppServer.ReportClientDocumentWrapper.VerifyDatabase()
at CrystalDecisions.CrystalReports.Engine.ReportDocument.VerifyDatabase()
I verified that the connection is changing in my development environment by doing the following:
Creating a copy of my datasource
In the designer, setting the datasource to the copied database
Deleting the copy
Previewing the report in the designer fails to load (as expected)
Generating the report using my utility class
The last step successfully generates the report which tells me that the datasource is changing, otherwise I would expect a logon failure.
What makes this particularly frustrating is that some reports in the non-developing environments work fine whereas some are producing the exception. So I don't understand why it works for some, but not for all.
Update
If I change my connection string in my development environment to the connection string used in Azure or in the On-Prem, it still works in my development environment. I really don't believe that the issue is with my connection string, but then again I have no idea what is causing the logon failure at this point.
After several days of looking into this, I finally found out what was causing the issue.
The particular report that was failing was using the MSOLEDBSQL provider instead of SQLOLEDB.
To fix the issue, I:
Opened the Crystal Report in Visual Studio
Right-clicked on the report
Database > Set Datasource Location
Expanded the properties of the current datasource
Double-clicked on the provider
Changed the value from MSOLEDBSQL to SQLOLEDB
Reentered the logon credentials (even though they get changed at runtime)
Saved the report
Build the solution
Republished the project
I am having a C# API that takes an Excel sheet (filePath, sheetName) as an input and return the sheet content as the output in JSON form.
The API works fine when I try to test it on my machine that contains windows server 2016 installed on it. But it always fail when I try to send the file path and sheet name from the form.
This is my API Code...
public IHttpActionResult GetSheetData(string filePath, string sheetName)
{
try
{
var connectionString = $#"
Provider=Microsoft.ACE.OLEDB.12.0;
Data Source={filePath};
Extended Properties=""Excel 12.0 Xml;HDR=YES""";
using (var conn = new OleDbConnection(connectionString))
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText = $#"SELECT * FROM [{sheetName}$]";
using (var DRow = cmd.ExecuteReader())
{
var query = DRow.Cast<DbDataRecord>().Select(row => new
{
name = row[0],
age = row[1],
email = row[2]
});
var JSON = JsonConvert.SerializeObject(query);
return Ok(JSON);
}
}
}
catch (Exception) { return Ok(HttpStatusCode.NotFound); };
}
The JSON result is returned perfectly when I test the API on the Server, But when I try to test it using this form..
It always returns 404 (Not Found).
This is my sample excel sheet data.
Any Ideas?!
I have found the solution to my Issue, and I wanted to share with you.
The Issue was all about the access rights of the IIS.
When I try to access a file on my local machine or in the clients machine, the IIS App Pool must have an access rights on that location.
So, I used the following steps.
Browse to the folder containing the file -to be read-
Right Click the folder and select 'Properties'
Under 'Security' Tab, select Edit.
In the 'Edit' Window select 'Add' -> 'Advanced' -> Find Now
Look up the result names while you find the user related to IIS -[IIS-IUsers]- in my case.
allow access controls to the folder -full access- or -Read and Write- etc...
I Hope this is helpful for you.
Thank you all.
I try to copy a database using smo, but I get the error:
"User, group, or role '%' already exists in the current database"
My code:
var conn = GetServerConnection();
var server = new Server(conn);
var sourceDb = server.Databases[sourceDatabase.Name];
var destinationDbName = GetNameForDatabase(dbName);
var destinationDb = new Database(server, destinationDbName);
destinationDb.Create();
var transfer = new Transfer(sourceDb) {
DestinationDatabase = destinationDbName,
DestinationServer = server.Name,
DestinationLoginSecure = true,
CopySchema = true,
CopyAllTables = true,
CopyData = true,
CopyAllUsers = false,
};
transfer.Options.WithDependencies = true;
transfer.Options.ContinueScriptingOnError = true;
transfer.TransferData();
Thanks in advance for any suggestions!
Do you have any mappings from the database server to the source database? Try removing those before attempting the copy.
I was copying the database using the Copy Database wizard in order to create a test database on the same server, and got this error. The problem was that the source database had a user login mapped to that database. Somewhere in the mix of it all, the Copy Database wizard was trying to add a user to the destination database via a straight copy, but also add the same user through the mapping. The trick was to remove the mapping of the source database, then copy the database, then add the mapping back to the source (it was already added at the destination).
I think I have a straight forward question. I'm writing a system that allows users from company A to single sign on to the system and for this I go back to the central database of users at company A and validate the user credentials passed to me.
Currently my implementation involves building up my query using a stringbuilder and then passing the string as command text. My question is; is there a nicer way of doing this. below is my code;
public User LoginSSO(string UserName, Int32 sectorCode)
{
using (OdbcConnection con = new OdbcConnection(ConfigurationManager.ConnectionStrings["ComapnyA"].ConnectionString))
{
con.Open();
StringBuilder sb = new StringBuilder();
sb.AppendLine("Select mx.Id, mx.UserName, mx.firstname,mx.surname,mx.sectorCode,");
sb.AppendLine("mx.deleteFlag, dn.sectorGroupCode, dn.region, dn.district");
sb.AppendLine("from users mx");
sb.AppendLine("Inner Join sector dn on dn.sectorCode = mx.sectorCode");
sb.AppendLine("Where (mx.UserName = '{0}')");
string commandText = string.Format(sb.ToString(), UserName, sectorCode);
using (OdbcCommand comm = new OdbcCommand(commandText, con))
{
using (OdbcDataReader reader = comm.ExecuteReader())
{
if (reader.Read())
{
User user = new User();
user.Id = Convert.ToInt32(reader["Id"]);
user.Username = Convert.ToString(reader["UserName"]);
user.Firstname = Convert.ToString(reader["firstname"]);
user.Surname = Convert.ToString(reader["surname"]);
_dealerGroupCode = Convert.ToString(reader["sectorGroupCode"]);
_region = Convert.ToInt32(reader["region"]);
_district = Convert.ToInt32(reader["district"]);
_dealerCode = dealerCode;
_accessLevel = AccessLevel.Sector;
return user;
}
}
}
}
return null;
}
I don't like the fact that I am building up my sql which is ultimately a static script. Please note that I can't manipulate the remote server in any way or add any stored procedures to it. For the rest of the app I have been using LINQ but I'm assuming that isn't an option.
This is the most low-level way of querying a database with ADO.NET. Open connection, send command, read out results. You should however use parametrized queries instead of String.Format, since that will open up your program to SQL injection. Just consider what would happen if UserName has a ' character in it. The following would be much better:
string sql = #"Select mx.Id, mx.UserName, mx.firstname, mx.surname,
mx.sectorCode, mx.deleteFlag, dn.sectorGroupCode,
dn.region, dn.district
From users mx
Inner Join sector dn on dn.sectorCode = mx.sectorCode
Where (mx.UserName = ?)";
var command = new OleDbCommand(sql);
command.Parameters.AddWithValue(0, UserName);
If you want a higher level interface, look into DataSets/DataAdapters. They aren't as fancy as LINQ, but they'll give you an easy fill/update, and work with any database adapter. If you're using Visual Studio, you even get a visual designer that can generate Typed Datasets in drag-and-drop fashion that'll give you strong-typed accessors for all your data.
You might also want to look into the native MySql connector classes, instead of using ODBC.
You can use ‘sp_addlinkedserver’ system store procedure to link to the remote server server and then fire a query. following is the sample command.:
EXEC sp_addlinkedserver
#server = ‘SourceServer’
, #Srvproduct = ”
, #Provider = ‘SQLNCLI’
, #datasrc = ‘Remote SQL Server instance name’
I suggest you to please refer following link to know about how to run sql query on remote server http://ashishkhandelwal.arkutil.com/sql-server/sql-query-to-the-remote-sql-server/
I'm using Access 2007 and C# to learn Databases. So far it's been rough but I've been able to handle things relatively well. What I need to do though is to query a my database table Accounts for the Amount of money a user has based on their pin. I've placed a button on the Windows Form I am using that will query the database on click. When I run/click the button as per normal I recieve the following error.
Essentially my question is this: How would I go about setting the permissions up so that my program can freely access the Access Database I have?
My Exception Error:
Exception: System.Data.OleDb.OleDbException: The Microsoft Office Access database engine cannot open or write to the file 'C:\Users\Public'. It is already opened exclusively by another user, or you need permission to view and write its data.
My code:
public partial class frmPin : Form
{
static string connString = #"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\Public;Persist Security Info=True";
static private int pin = 11; //The First Pin in my records, for debugging I inserted it directly.
static string selectStatement = "SELECT Amount FROM Accounts WHERE(PIN=" + pin + ")";
OleDbConnection conn = new OleDbConnection(connString);
OleDbCommand cmd = new OleDbCommand(selectStatement);
public frmPin()
{
InitializeComponent();
}
private void btnQry_Click(object sender, EventArgs e)
{
try
{
conn.Open();
OleDbDataReader reader = cmd.ExecuteReader(); // executes query
while (reader.Read()) // if can read row from database
{
txtBx.Text = reader.GetValue(1).ToString();
}
}
catch (Exception ex)
{
txtBx.Text = "Exception: " + ex; // Displays Exception
}
finally
{
conn.Close(); // finally closes connection
}
}
"C:\Users\Public" needs to be changed to the actual path of the *.mdb file you want to access:
"C:\Users\Public.mdb"
OR
"C:\Users\Public\Something.mdb"
Depending on the name of your database:
Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\mydatabase.mdb;User Id=admin;Password=;
Or it may be an *.accdb file. Such as:
Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\myFolder\myAccess2007file.accdb;Persist Security Info=False;
See http://www.connectionstrings.com/access-2007 and http://www.connectionstrings.com/access
Also, sometimes you will get this kind of problem if you have the file open in another program like Access 2007, the file is marked as Read Only, or the security permissions are such that you don't have Read or Write Access. Note that if you set a "Deny" permission (in the filesystem/NTFS) for a group like Users, then it will override all other permissions, such that an Administrator would be effected by the Deny permission.
Edit: Thanks for comments, added a little clarification.