Adding/Removing SQLite database password programmatically - c#

Trying to create functions to programmatically add a password to the sqlite database, and remove it - to get rid of the password when in debug.
I followed the steps of the best post explaining it (https://stackoverflow.com/a/1385690/6617804) and all the other sources that I found are using the same process.
What I am doing:
The connection strings:
private static string AuthoringDbFullPath = System.IO.Path.Combine(DataFolder, "Authoring.db");
private static string AuthoringConnectionStringWithPw = #"Data Source=" + AuthoringDbFullPath + "; Password=" + DbPassword +";";
private static string AuthoringConnectionStringWithoutPw = #"Data Source=" + AuthoringDbFullPath+";";
private static string AuthoringConnectionString = AuthoringConnectionStringWithPw;
The enable password function:
public static bool EnableDbPassword()
{
try
{
//The Connection must not open closed to set a password
Connection = new SQLiteConnection(AuthoringConnectionStringWithoutPw);
Connection.SetPassword(DbPassword);
Connection = new SQLiteConnection(AuthoringConnectionStringWithPw);
Connection.Open();
Connection.Close();
if (Connection != null && Connection?.State == System.Data.ConnectionState.Open)
{
Connection.SetPassword(DbPassword);
AuthoringConnectionString = AuthoringConnectionStringWithPw;
return true;
}
}
catch (Exception ex)
{
LogManager.LogException(ex);
}
return false;
}
The disable password function:
public static bool DisableDbPassword()
{
try
{
//if not connected, it does
if (Connection == null || Connection?.State != System.Data.ConnectionState.Open)
{
Connection = new SQLiteConnection(AuthoringConnectionStringWithPw);
Connection.Open();
}
Connection.Close();
Connection.Open();
if (Connection != null && Connection?.State == System.Data.ConnectionState.Open)
{
Connection.ChangePassword("");
AuthoringConnectionString = AuthoringConnectionStringWithoutPw;
return true;
}
}
catch (Exception ex)
{
LogManager.LogException(ex);
}
return false;
}
Two things happening:
After having add the password (with no apparent issue), I can still open the database in SQLiteStudio as if there were still no password:
When trying to disable after having enabled it, it raised a System.Data.SQLite.SqliteException exception when executing Connection.ChangePassword("");
System.Data.SQLite.SqliteException :
Message "file is not a database. not an error
How to create a simple architecture being able to add and remove a password on a SQLite database?

Related

Exception handling quandry

I am throwing a new exception when a database row is not found.
Class that was called:
public ProfileBO retrieveProfileByCode(string profileCode)
{
return retrieveSingleProfile("profile_code", profileCode);
}
private ProfileBO retrieveSingleProfile(string termField, string termValue)
{
ProfileBO profile = new ProfileBO();
//Query string is temporary. Will make this a stored procedure.
string queryString = " SELECT * FROM GamePresenterDB.gp.Profile WHERE " + termField + " = '" + termValue + "'";
using (SqlConnection connection = new SqlConnection(App.getConnectionString()))
{
connection.Open();
SqlCommand command = new SqlCommand(queryString, connection);
SqlDataReader reader = command.ExecuteReader();
if (reader.Read())
{
profile = castDataReadertoProfileBO(reader, profile);
}
else
{
// No record was selected. log it and throw the exception (We'll log it later, for now just write to console.)
Console.WriteLine("No record was selected from the database for method retrieveSingleProfile()");
throw new InvalidOperationException("An exception occured. No data was found while trying to retrienve a single profile.");
}
reader.Close();
}
return profile;
}
However, when I catch the exception in the calling class, 'e' is now null. What am I doing wrong? I believe this works fine in Java, so C# must handle this differently.
Calling class:
private void loadActiveProfile()
{
try
{
ProfileBO profile = profileDAO.retrieveProfileByCode(p.activeProfileCode);
txtActiveProfileName.Text = profile.profile_name;
}
catch (InvalidOperationException e)
{
}
}
Now all the code has been put in the question, you can move the try catch outside of your 'loadActiveProfile' method and place it into 'retrieveSingleProfile'.
private void loadActiveProfile()
{
ProfileBO profile = profileDAO.retrieveProfileByCode(p.activeProfileCode);
txtActiveProfileName.Text = profile.profile_name;
}
removed the try catch^
private ProfileBO retrieveSingleProfile(string termField, string termValue)
{
try {
ProfileBO profile = new ProfileBO();
//Query string is temporary. Will make this a stored procedure.
string queryString = " SELECT * FROM GamePresenterDB.gp.Profile WHERE " + termField + " = '" + termValue + "'";
using (SqlConnection connection = new SqlConnection(App.getConnectionString()))
{
connection.Open();
SqlCommand command = new SqlCommand(queryString, connection);
SqlDataReader reader = command.ExecuteReader();
if (reader.Read())
{
profile = castDataReadertoProfileBO(reader, profile);
}
else
{
// No record was selected. log it and throw the exception (We'll log it later, for now just write to console.)
Console.WriteLine("No record was selected from the database for method retrieveSingleProfile()");
throw new InvalidOperationException("An exception occured. No data was found while trying to retrienve a single profile.");
}
reader.Close();
}
return profile;
}
catch(InvalidOperationException e)
{
}
}
Added try catch in the correct place.
You need to step into the catch block for e to be set to the thrown InvalidOperationException:
catch (System.InvalidOperationException e)
{
int breakPoint = 0; //<- set a breakpoint here.
//Either you reach the breakpoint and have an InvalidOperationException, or you don't reach the breakpoint.
MessageBox.Show(e.Message);
}
Also make sure that the InvalidOperationException you throw is actually a System.InvalidOperationException and not some custom type of yours called "InvalidOperationException".
Like #Clemens said, you need to show all the relevant code.
As a quick test, this works just fine:
class Program
{
static void Main(string[] args)
{
try
{
Console.WriteLine("Throwing error");
ThrowException();
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.Message);
}
Console.ReadKey(true);
}
static void ThrowException()
{
throw new InvalidOperationException("Blah blah blah");
}
}

Closing MySql datareader connection

So this is a little bit code-ceptionlike.
I have a function that is checking the last ID in a table, this function is called within another function. At the end of that function, I have another function that's opening another datareader.
Error:
There is already an open Datareader associated with this connection which must be closed first.
getLastIdfromDB()
public string getLastIdFromDB()
{
int lastIndex;
string lastID ="";
var dbCon = DB_connect.Instance();
if (dbCon.IsConnect())
{
MySqlCommand cmd2 = new MySqlCommand("SELECT ID FROM `competitor`", dbCon.Connection);
try
{
MySqlDataReader reader = cmd2.ExecuteReader();
while (reader.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.Message);
}
}
return lastID;
}
This function is later-on used in this function:
private void addPlayerBtn_Click(object sender, EventArgs e)
{
ListViewItem lvi = new ListViewItem(getLastIdFromDB());
.........................................^
... HERE
...
... irrelevant code removed
.........................................
var dbCon = DB_connect.Instance();
if (dbCon.IsConnect())
{
MySqlCommand cmd = new MySqlCommand("INSERT INTO `competitor`(`ID`, `Name`, `Age`) VALUES(#idSql,#NameSql,#AgeSql)", dbCon.Connection);
cmd.Parameters.AddWithValue("#idSql", getLastIdFromDB());
cmd.Parameters.AddWithValue("#NameSql", playerName.Text);
cmd.Parameters.AddWithValue("#AgeSql", playerAge.Text);
try
{
cmd.ExecuteNonQuery();
listView1.Items.Clear();
}
catch (Exception ex)
{
MessageBox.Show("Error:" + ex.Message);
dbCon.Connection.Close();
}
finally
{
updateListView();
}
}
}
What would be the best way for me to solve this problem and in the future be sure to close my connections properly?
UPDATE: (per request, included DB_connect)
class DB_connect
{
private DB_connect()
{
}
private string databaseName = "simhopp";
public string DatabaseName
{
get { return databaseName; }
set { databaseName = value; }
}
public string Password { get; set; }
private MySqlConnection connection = null;
public MySqlConnection Connection
{
get { return connection; }
}
private static DB_connect _instance = null;
public static DB_connect Instance()
{
if (_instance == null)
_instance = new DB_connect();
return _instance;
}
public bool IsConnect()
{
bool result = true;
try
{
if (Connection == null)
{
if (String.IsNullOrEmpty(databaseName))
result = false;
string connstring = string.Format("Server=localhost; database={0}; UID=root;", databaseName);
connection = new MySqlConnection(connstring);
connection.Open();
result = true;
}
}
catch (Exception ex)
{
Console.Write("Error: " + ex.Message);
}
return result;
}
public void Close()
{
connection.Close();
}
}
}
You are trying to have multiple open readers on the same connection. This is commonly called "MARS" (multiple active result sets). MySql seems to have no support for it.
You will have to either limit yourself to one open reader at a time, or use more than one connection, so you can have one connection for each reader.
My suggestion would be to throw away that singleton-like thingy and instead use connection pooling and proper using blocks.
As suggested by Pikoh in the comments, using the using clause indeed solved it for me.
Working code-snippet:
getLastIdFromDB
using (MySqlDataReader reader2 = cmd2.ExecuteReader()) {
while (reader2.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
Your connection handling here is not good. You need to ditch the DB_connect. No need to maintain a single connection - just open and close the connection each time you need it. Under the covers, ADO.NET will "pool" the connection for you, so that you don't actually have to wait to reconnect.
For any object that implements IDisposable you need to either call .Dispose() on it in a finally block, or wrap it in a using statement. That ensures your resources are properly disposed of. I recommend the using statement, because it helps keep the scope clear.
Your naming conventions should conform to C# standards. Methods that return a boolean should be like IsConnected, not IsConnect. addPlayerBtn_Click should be AddPlayerButton_Click. getLastIdFromDB should be GetlastIdFromDb or getLastIdFromDatabase.
public string GetLastIdFromDatabase()
{
int lastIndex;
string lastID ="";
using (var connection = new MySqlConnection(Configuration.ConnectionString))
using (var command = new MySqlCommand("query", connection))
{
connection.Open();
MySqlDataReader reader = cmd2.ExecuteReader();
while (reader.Read())
{
string item = reader2["ID"].ToString();
lastIndex = int.Parse(item);
lastIndex++;
lastID = lastIndex.ToString();
}
}
return lastID;
}
Note, your query is bad too. I suspect you're using a string data type instead of a number, even though your ID's are number based. You should switch your column to a number data type, then select the max() number. Or use an autoincrementing column or sequence to get the next ID. Reading every single row to determine the next ID and incrementing a counter not good.

SQL ParameterDirection.InputOutput / SqlCommand.ExecuteNonQuery() error

I have a very strange problem that only occurs when the code in question is in a high load situations. My ASP.NET web client C# code calls a T-SQL stored procedure that has an OUTPUT parameter.
Under high loads, the data being returned sometimes does not make it back to the calling C# code. I have extracted all the relevant code into the example below;
Stored procedure:
CREATE PROCEDURE GetLoginName
#LoginId BIGINT,
#LoginName VARCHAR(50) OUTPUT
AS
SET NOCOUNT ON
SELECT #LoginName = LoginName
FROM Logins
WHERE Id = #LoginId
SET NOCOUNT OFF
GO
Database base class:
public class DatabaseContextBase : IDisposable
{
private SqlConnection _connection;
private string _connectionString;
private SqlInt32 _returnValue;
private int _commandTimeout;
private static int _maxDatabaseExecuteAttempts = 3;
private static int s_commandTimeout = 30;
protected DBContextBase()
{
// try and get the connection string off the identity first...
string connectionString = GetConnectionStringFromIdentity();
if(connectionString != null)
{
ConstructionHelper(connectionString, s_commandTimeout);
}
else
{
// use the initialised static connection string, and call the other overload
// of the constructor
ConstructionHelper(s_connectionString, s_commandTimeout);
}
}
private void ConstructionHelper( string connectionString, int commandTimeout )
{
// store the connection string in a member var.
_connectionString = connectionString;
// store the timeout in a member var.
_commandTimeout = commandTimeout;
}
public static string GetConnectionStringFromIdentity()
{
IIdentity identity = Thread.CurrentPrincipal.Identity as wxyz.Security.wxyzIdentityBase;
string connectionString = null;
if(identity != null)
{
connectionString = ((wxyz.Security.wxyzIdentityBase) identity ).ConnectionString;
}
return connectionString;
}
public void Dispose()
{
if (_connection.State != ConnectionState.Closed)
{
_connection.Close();
}
_connection.Dispose();
_connection = null;
}
protected void ExecuteNonQuery(SqlCommand command)
{
SqlConnection con = this.Connection;
lock (con)
{
if (con.State != ConnectionState.Open)
{
con.Open();
}
// don't need a try catch as this is only ever called from another method in this
// class which will wrap it.
command.Connection = con;
command.Transaction = _transaction;
command.CommandTimeout = _commandTimeout;
for (int currentAttempt = 1; currentAttempt == _maxDatabaseExecuteAttempts; currentAttempt++)
{
try
{
// do it
command.ExecuteNonQuery();
// done, exit loop
break;
}
catch (SqlException sex)
{
HandleDatabaseExceptions(currentAttempt, sex, command.CommandText);
}
}
}
}
protected void HandleDatabaseExceptions(int currentAttempt, SqlException sqlException, string sqlCommandName)
{
if (DataExceptionUtilities.IsDeadlockError(sqlException))
{
if (!this.IsInTransaction)
{
// Not in a transaction and a deadlock detected.
// If we have not exceeded our maximum number of attemps, then try to execute the SQL query again.
string deadlockMessage = string.Format("Deadlock occured in attempt {0} for '{1}':", currentAttempt, sqlCommandName);
Logging.Write(new ErrorLogEntry(deadlockMessage, sqlException));
if (currentAttempt == DBContextBase.MaxDatabaseExecuteAttempts)
{
// This was the last attempt so throw the exception
throw sqlException;
}
// Wait for a short time before trying again
WaitShortAmountOfTime();
}
else
{
// We're in a transaction, then the calling code needs to handle the deadlock
string message = string.Format("Deadlock occured in transaction for '{0}':", sqlCommandName);
throw new DataDeadlockException(message, sqlException);
}
}
else if (this.IsInTransaction && DataExceptionUtilities.IsTimeoutError(sqlException))
{
// We're in a transaction and the calling code needs to handle the timeout
string message = string.Format("Timeout occured in transaction for '{0}':", sqlCommandName);
// Raise a Deadlock exception and the calling code will rollback the transaction
throw new DataDeadlockException(message, sqlException);
}
else
{
// Something else has gone wrong
throw sqlException;
}
}
/// <summary>
/// get the SqlConnection object owned by this database (already connected to db)
/// </summary>
public SqlConnection Connection
{
get {
// check whether we've got a connection string (from either identity or static initialise)
if ( _connectionString == null )
{
throw new ArgumentNullException( "connectionString", "Connection string not set" );
}
if ( _connection != null )
{
return _connection;
}
else
{
_connection = new SqlConnection( _connectionString );
return _connection;
}
}
}
/// <summary>
/// Return value from executed stored procedure
/// </summary>
public SqlInt32 ReturnValue
{
get { return _returnValue; }
set { _returnValue = value; }
}
}
Database access class:
public class AuthenticationDBCommands
{
public static SqlCommand GetLoginName()
{
System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand("GetLoginName");
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add(new SqlParameter("#RETURN_VALUE", System.Data.SqlDbType.Int, 0, System.Data.ParameterDirection.ReturnValue, false, 0, 0, "RETURN_VALUE", System.Data.DataRowVersion.Current, SqlInt32.Null));
cmd.Parameters.Add(new SqlParameter("#LoginId", SqlDbType.BigInt, 8, ParameterDirection.Input, false, 0, 0, "LoginId", DataRowVersion.Current, SqlInt64.Null));
cmd.Parameters.Add(new SqlParameter("#LoginName", SqlDbType.VarChar, 50, ParameterDirection.InputOutput,false, 0, 0, "LoginName", DataRowVersion.Current, SqlString.Null));
return cmd;
}
}
public class AuthenticationDBContext : DatabaseContextBase
{
public AuthenticationDBContext() : base()
{
}
public void GetLoginName(SqlInt64 LoginId, ref SqlString LoginName)
{
SqlCommand cmd = AuthenticationDBCommands.GetLoginName();
cmd.Parameters[1].Value = LoginId;
cmd.Parameters[2].Value = LoginName;
base.ExecuteNonQuery(cmd);
base.ReturnValue = (SqlInt32) cmd.Parameters[0].Value;
LoginName = (SqlString)(cmd.Parameters[2].Value);
}
}
So when it's used inside the ASP.NET web client it look like this:
protected string GetLoginName(long loginId)
{
SqlString loginName = SqlString.Null;
using (AuthenticationDBContext dbc = new AuthenticationDBContext())
{
dbc.GetLoginName(loginId, ref loginName);
}
return loginName.Value;
}
As you can see this is fairly standard stuff. But when the AuthenticationDBContext.GetLoginName() method is called by many different users in quick succession the loginName object is sometimes null.
When the SqlCommand.ExecuteNonQuery() fails to return any data it does not throw an exception.
I have tested the SQL and it always finds a value (I've inserted #LoginName into a table and it's never null). So the problem is happening after or in SqlCommand.ExecuteNonQuery();
As I said, this is an example of what it happening. In reality, data is not being returned for lots of different stored procedures.
It's also worth stating that other web clients that share the app pool in IIS are not affected when the web client in question is under a heavy load.
I'm using .NET 4.5 and my database is on SQL Server 2008.
Has anyone seen anything like this before?
Can anyone recommend any changes?
Thanks in advance,
Matt
UPDATE. Thanks for all your comments. I have made the following change to the DatabaseContextBase class.
private void ExecuteNonQueryImpl(SqlCommand command)
{
object _lockObject = new object();
lock (_lockObject)
{
SqlConnection con = this.GetConnection();
if (con.State != ConnectionState.Open)
{
con.Open();
}
// don't need a try catch as this is only ever called from another method in this
// class which will wrap it.
command.Connection = con;
command.Transaction = _transaction;
command.CommandTimeout = _commandTimeout;
for (int currentAttempt = 1; currentAttempt <= _maxDatabaseExecuteAttempts; currentAttempt++)
{
try
{
// do it
command.ExecuteNonQuery();
// done, exit loop
break;
}
catch (SqlException sex)
{
HandleDatabaseExceptions(currentAttempt, sex, command.CommandText);
}
}
if (!this.IsInTransaction)
{
con.Close();
}
}
}
public SqlConnection GetConnection()
{
if (this.IsInTransaction)
{
return this.Connection;
}
else
{
// check whether we've got a connection string (from either identity or static initialise)
if ( _connectionString == null )
{
string exceptionMessage = Language.Translate("DbContextNotInitialized");
throw new ArgumentNullException( "connectionString", exceptionMessage );
}
return new SqlConnection(_connectionString);
}
}
However, in a load test the data still sometimes comes back as null. The web client is not working in a transaction so a new SqlConnection object is created, opened and closed every time a call is made. (there are other areas of code which share the DatabaseContextBase class that do work in a transaction so the original connection property is needed)
I would like to mention that again that I'm confident that the store procedure is working correctly as I have inserted the #LoginName value into a table and it's never null.
Thanks,
Matt
Your "for loop" definition is not correct.
for (int currentAttempt = 1; currentAttempt == _maxDatabaseExecuteAttempts; currentAttempt++)
This will initialize currentAttempt to 1, run the loop, increment currentAttempt, and then check to see if currentAttempt is equal to 3, which it isn't, and exit the loop. I think what you want is
for (int currentAttempt = 1; currentAttempt <= _maxDatabaseExecuteAttempts; currentAttempt++)

Metro Application connects to SQL via WCF Service - Error

I have created a new metro application for Windows 8.
My intention was to consume data from the WCF data service in the metro application. The WCF service should connect to a SQL server installed on the local machine.
I have set up an Interface for my service so I can pass parameters to the methods I want the WCF to execute and then return results back to the Metro Application afterwards.
I followed a few different tutorials online and had success, until...
A network-related or instance-specific error occurred while
establishing a connection to SQL Server. The server was not found or
was not accessible. Verify that the instance name is correct and that
SQL Server is configured to allow remote connections. (provider: Named
Pipes Provider, error: 40 - Could not open a connection to SQL Server)
I have a included the code from both the Login landing Page and the SQL configuration page. When the metro application launches, it runs a method to check if there is a connection. If no connection is found it displays the SQL configuration page.
The application launches. OnNavigate to the login form the CheckConnection() method returns true. But when I enter user details and press login I simply get an error on the following line of code in the async void Login() method on the Login Page
newCustomType = await dataServiceClientName.CheckLoginAsync(txtUsername.Text, txtPassword.Text);
If anyone understands why this error is displaying the help would be greatly appreciated.
I have included the relevant code (or what I can tell is relevant) below.
Thanks in advance :)
Code Snippets:
Abridged Service Interface
[ServiceContract]
public interface CTUServiceInterface
{
// TODO: Add your service operations here
//LoginForm
[OperationContract]
bool CheckConnection();
//SQL Config
[OperationContract]
bool UpdateConnectionDetails(string ConnectionString);
//LoginForm
[OperationContract]
LoginDetails CheckLogin(string Username, string Password);
//LoginForm Success
[OperationContract]
UserGlobalDetails GetActiveUserDetails(int UserKey);
}
[DataContract]
public class LoginDetails
{
bool loginSuccess = false;
string errorMessage = "";
int userKey = -1;
[DataMember]
public bool LoginSuccess
{
get { return loginSuccess; }
set { loginSuccess = value; }
}
[DataMember]
public string ErrorMessage
{
get { return errorMessage; }
set { errorMessage = value; }
}
[DataMember]
public int UserKey
{
get { return userKey; }
set { userKey = value; }
}
}
[DataContract]
public class UserGlobalDetails
{
string firstName = "";
string lastName = "";
int departmentKey = -1;
bool manager = false;
[DataMember]
public string FirstName
{
get { return firstName; }
set { firstName = value; }
}
[DataMember]
public string LastName
{
get { return lastName; }
set { lastName = value; }
}
[DataMember]
public int DepartmentKey
{
get { return departmentKey; }
set { departmentKey = value; }
}
[DataMember]
public bool Manager
{
get { return manager; }
set { manager = value; }
}
}
Abridged Service Code
public class CTUService : CTUServiceInterface
{
private static SqlConnection localConnection = new SqlConnection("Data Source=PC-VIRTUAL;Initial Catalog=CarTraUnl;Integrated Security=True");
//LoginForm
public bool CheckConnection()
{
bool result = true;
try
{
localConnection.Open();
localConnection.Close();
}
catch(Exception ex)
{
result = false;
throw ex;
}
return result;
}
//SQL Config
public bool UpdateConnectionDetails(string ConnectionString)
{
localConnection = new SqlConnection(ConnectionString);
return CheckConnection();
}
//LoginForm
public LoginDetails CheckLogin(string Username, string Password)
{
//Initilize the LoginDetails Object to return with results
LoginDetails localDetails = new LoginDetails();
//Initilize the localDataTable to hold sql results
DataTable localDataTable = new DataTable("localDataTable");
//Setup a SqlDataAdapter and get info from the 'UserGlobal' Table
localConnection.Open();
SqlDataAdapter localDataAdapter = new SqlDataAdapter(
"SELECT [Password],[Key]" +
"FROM [CarTraUnl].[dbo].[UserGlobal]" +
"WHERE [Username] = '" + Username + "'"
, localConnection);
localConnection.Close();
//Fill localDataTable with information from the UserGlobal Table
localDataAdapter.Fill(localDataTable);
//Set loginStatus equal to the number of passwords found for the given username
int loginStatus = localDataTable.Rows.Count;
//If no passwords are found, there was no username like the given one
if (loginStatus == 0)
{
localDetails.ErrorMessage = "Invalid Username";
}
//If one password was found, check if it matches the given password
else if (loginStatus == 1 && localDataTable.Rows[0][0].ToString() == Password)
{
localDetails.LoginSuccess = true;
localDetails.ErrorMessage = "";
localDetails.UserKey = int.Parse(localDataTable.Rows[0][1].ToString());
}
//If one password is found, but it doesn't match show the error
else if (loginStatus == 1 && localDataTable.Rows[0][0].ToString() != Password)
{
localDetails.ErrorMessage = "Invalid Password";
}
//If multiple passwords are found, there are duplicate usernames
else if (loginStatus > 1)
{
localDetails.ErrorMessage = "Duplicate Usernames";
}
return localDetails;
}
//LoginForm Success
public UserGlobalDetails GetActiveUserDetails(int UserKey)
{
//Initilize UserGlobalDetails object to return later
UserGlobalDetails localUserGlobalDetails = new UserGlobalDetails();
//Initilize a DataTable to hold our sql results
DataTable localDataTable = new DataTable("localDataTable");
//Setup a SqlDataAdapter and get info from the 'UserGlobal' Table
SqlDataAdapter localDataAdapter = new SqlDataAdapter(
"SELECT [FirstName],[LastName],[DepartmentKey],[Manager]" +
"FROM [CarTraUnl].[dbo].[UserGlobal]" +
"WHERE [Key] = '" + UserKey + "'"
, localConnection);
//Fill localDataTable with information from the 'UserGlobal' Table
localDataAdapter.Fill(localDataTable);
//Configure the UserGlobalDetails object 'localUserGlobalDetails' with the retrived results
localUserGlobalDetails.FirstName = localDataTable.Rows[0][0].ToString();
localUserGlobalDetails.LastName = localDataTable.Rows[0][1].ToString();
localUserGlobalDetails.DepartmentKey = int.Parse(localDataTable.Rows[0][2].ToString());
localUserGlobalDetails.Manager = bool.Parse(localDataTable.Rows[0][3].ToString());
//return the results
return localUserGlobalDetails;
}
}
Login.xaml.cs
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
protected override void OnNavigatedTo(NavigationEventArgs e)
{
lblError.Text = "";
}
private async void CheckConnection()
{
CTUServiceReference.CTUServiceInterfaceClient dataServiceClientName = new CTUServiceReference.CTUServiceInterfaceClient();
bool result = await dataServiceClientName.CheckConnectionAsync();
if (result == false)
{
this.Frame.Navigate(typeof(SqlConfig));
}
}
private void btnLogin_Click(object sender, RoutedEventArgs e)
{
Login();
}
private async void Login()
{
btnLogin.IsEnabled = false;
CTUServiceReference.CTUServiceInterfaceClient dataServiceClientName = new CTUServiceReference.CTUServiceInterfaceClient();
CTUServiceReference.LoginDetails newCustomType = new CTUServiceReference.LoginDetails();
newCustomType = await dataServiceClientName.CheckLoginAsync(txtUsername.Text, txtPassword.Text);
CTUServiceReference.UserGlobalDetails currentActiveUserDetails = new CTUServiceReference.UserGlobalDetails();
currentActiveUserDetails = await dataServiceClientName.GetActiveUserDetailsAsync(newCustomType.UserKey);
//The globalSettings.cs file is not included, but it just holds persistent application data
globalSettings.currentUserKey = newCustomType.UserKey;
globalSettings.firstName = currentActiveUserDetails.FirstName;
globalSettings.lastName = currentActiveUserDetails.LastName;
globalSettings.departmentKey = currentActiveUserDetails.DepartmentKey;
globalSettings.manager = currentActiveUserDetails.Manager;
if (newCustomType.LoginSuccess)
{
//This page is where we go when login is successful
this.Frame.Navigate(typeof(FormSelector));
}
else
{
lblError.Text = newCustomType.ErrorMessage;
btnLogin.IsEnabled = true;
}
}
SQLConfig.xaml.cs
private void btnSqlConfigSave_Click(object sender, RoutedEventArgs e)
{
saveConfig();
}
private async void saveConfig()
{
btnSave.IsEnabled = false;
string connectionstring = "";
if (rbAutomatic.IsChecked == true)
{
//Integrated Secturity
connectionstring = "Data Source=" + txtServer.Text + ";Initial Catalog= " + txtDB.Text + ";Integrated Security=True";
}
else
{
//Standard Authentication with username and password
connectionstring = "Data Source=" + txtServer.Text + ";Initial Catalog= " + txtDB.Text + ";User Id=" + txtUser.Text + ";Password=" + txtPass.Text;
}
CTUServiceReference.CTUServiceInterfaceClient dataServiceClientName = new CTUServiceReference.CTUServiceInterfaceClient();
bool result = await dataServiceClientName.UpdateConnectionDetailsAsync(connectionstring);
if (result)
{
this.Frame.GoBack();
return;
}
else
{
lblError.Text = "Config error";
btnSave.IsEnabled = true;
}
}
Version Info
Visual Studio
Microsoft Visual Studio Ultimate 2012
Version 11.0.50727.1 RTMREL
Microsoft .NET Framework Version 4.5.50709
Installed Version: Ultimate
SQL Server
Microsoft SQL Server Management Studio - 11.0.2100.60
Microsoft Analysis Services Client Tools - 11.0.2100.60
Microsoft Data Access Components (MDAC) - 6.2.9200.16384
Microsoft MSXML - 3.0 6.0
Microsoft .NET Framework - 4.0.30319.18051
Operating System - 6.2.9200
This error could have several causes but it means what it means... being, your service cannot access to:
"Data Source=" + txtServer.Text + ";
This is most probably not a problem in your code, but an SQLServer configuration issue.
I suggest you try the connectionString in an ODBC call first to see if you can access it like this from local and remote.
it seems like your services tries to connect via named pipes check in the SQLServer Configuration Manager to see if the protocol is started for your specific instance.
Also try to enable the TCP/IP protocol and check that it listens on the right port (1433).
It could also be you are not targeting the "Named Instance". ie:
"Data Source=MYSERVER"
instead of
"Data Source=MYSERVER\MyInstance"
hope this helps!

Prevent ODP.net connetion from being open with expired password

I'm working on a Silverlight application that uses oracle security to authenticate the users. (This is a business requirement so it can't be changed).
I do so by calling a WCF web service that attempts to open a connection to the database using the provided username and password. If the connection fails, I catch the exception and return a message to the user, here's the login code:
[OperationContract]
public LoginResult LogIn(string username, string password, DateTime preventCache)
{
var result = new List<string>();
try
{
connectionString = ConfigurationManager.ConnectionStrings["SecurityBD"].ToString();
connectionString = connectionString.Replace("[username]", username);
connectionString = connectionString.Replace("[password]",passowrd)
using (var connection = new Oracle.DataAccess.Client.OracleConnection())
{
connection.ConnectionString = connectionString;
connection.Open();
if (connection.State == System.Data.ConnectionState.Open)
{
connection.Close();
return new LoginResult(true, GetPermisos(username), preventCache);
}
else
{
return new LoginResult(false, null, preventCache);
}
}
}
catch (Oracle.DataAccess.Client.OracleException ex)
{
if (ex.Number == 1017)
{
return new LoginResult(new SecurityError("Wrong credentials.", ErrorType.InvalidCredentials));
}
//Password expired.
if (ex.Number == 28001)
{
return new LoginResult(new SecurityError("Password expired.", ErrorType.PasswordExpired));
}
//Acount is locked.
if (ex.Number == 28000)
{
return new LoginResult(new SecurityError("Account is locked.", ErrorType.AccountLocked));
}
else
{
return new LoginResult(new SecurityError("An error occurred while attempting to connect." + Environment.NewLine + "Error: " + ex.ToString(), ErrorType.UndefinedError));
}
}
catch (Exception exg)
{
return new LoginResult(new SecurityError("An error occurred while attempting to connect." + Environment.NewLine + "Error: " + exg.ToString(), ErrorType.UndefinedError));
}
}
If the connection fails because of an expired password, I show the corresponding message to the user and then prompt him for his old and new password, and then send the new credentials to a ChangePassword method on my web serivce.
[OperationContract]
public ChangePasswordResult ChangePassword(string username, string oldPasswrod, string newPassword)
{
string connectionString = string.Empty;
try
{
connectionString = ConfigurationManager.ConnectionStrings["SecurityBD"].ToString();
connectionString = connectionString.Replace("[username]", username);
connectionString = connectionString.Replace("[password]",passowrd)
using (var connection = new OracleConnection(connectionString))
{
connection.Open();
if (connection.State == System.Data.ConnectionState.Open)
{
connection.Close();
using (var newConnection = new Oracle.DataAccess.Client.OracleConnection(connectionString))
{
newConnection.OpenWithNewPassword(Cryptography.TransportDecrypt(newPassword));
if (newConnection.State == System.Data.ConnectionState.Open)
{
return new ChangePasswordResult(null);
}
}
}
return new ChangePasswordResult(new SecurityError("Couldn't connect to the database.", ErrorType.UndefinedError));
}
}
catch (OracleException ex)
{
if (ex.Number == 1017)
{
return new ChangePasswordResult(new SecurityError("Wrong password", ErrorType.InvalidCredentials));
}
//Password expired.
if (ex.Number == 28001)
{
using (var newConnection = new Oracle.DataAccess.Client.OracleConnection(connectionString))
{
try
{
newConnection.OpenWithNewPassword(Cryptography.TransportDecrypt(newPassword));
if (newConnection.State == System.Data.ConnectionState.Open)
{
return new ChangePasswordResult(null);
}
else
{
return new ChangePasswordResult(new SecurityError("No se pudo establecer una conexión con la base de datos", ErrorType.UndefinedError));
}
}
catch (Oracle.DataAccess.Client.OracleException oex)
{
if (oex.Number == 28003)
return new ChangePasswordResult(new SecurityError("You'r new password does not match the security requeriments.." + Environment.NewLine + oex.Message, ErrorType.PasswordNotChanged));
else
return new ChangePasswordResult(new SecurityError(oex.Message, ErrorType.UndefinedError));
}
}
}
//Acount is locked.
if (ex.Number == 28000)
{
return new ChangePasswordResult(new SecurityError("Account is locked.", ErrorType.AccountLocked));
}
else
{
return new ChangePasswordResult(new SecurityError("Couldn't establish a connection." + Environment.NewLine + "Error: " + ex.Message, ErrorType.UndefinedError));
}
}
catch
{
throw;
}
}
After I perform the change password operation, the user is still able to connect with the old password and he's not able to connect with the new password. Only after I restart the application the change seems to take effect.
I'm using oracle's ODP.net driver. With Microsoft's oracle client, the user is able to connect with both the new and the old password after the password change.
The preventCache parameter was there only to verify that there was no type of client cache. I send the current date from the client, and then return the same value from the web service to see if it actually changes with subsequent requests, and it does as expected.
I've tried listening to the InfoMessage event of the connection, to see if there's any warning, but doing this prevents the password expired exception from being risen, and the code never reaches the eventHandler.
I'm completely lost, this behavior seems very odd to me and I still haven't figured out the root cause for the problem.
I've tryied copying the LogIn and ChangePassword methods on a desktop (WPF) application and it behaves exactly the same. So i guess the problem is not in the silverlight client.
Ok, i've figured this out. Checking with Toad the connection reminded opend even after executing the Connection.Close() method. This behavior seems to be part of the connection pooling mechanism from oracle.
Including Pooling=false on the connection string solved the problem.

Categories

Resources