SQL Server validation returns wrong output - c#

I am working on login page with validation on a local server using SQL Server. I created a login page and sign up page, my sign up page works fine but the login page keeps showing an error of "User not activated"
Here is my code behind for loginpage
public partial class Login : System.Web.UI.Page
{
protected void Validate_User(object sender, EventArgs e)
{
int userId = 0;
string constr = `ConfigurationManager.ConnectionStrings["constr"].ConnectionString;`
using (SqlConnection con = new SqlConnection(constr))
{
using (SqlCommand cmd = new SqlCommand("Validate_User"))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#Username", Login1.UserName);
cmd.Parameters.AddWithValue("#Password", Login1.Password);
cmd.Connection = con;
con.Open();
userId = Convert.ToInt32(cmd.ExecuteScalar());
con.Close();
}
switch (userId)
{
case -1:
Login1.FailureText = "Username and/or password is incorrect.";
break;
case -2:
Login1.FailureText = "Account has not been activated.";
break;
default:
FormsAuthentication.RedirectFromLoginPage(Login1.UserName, Login1.RememberMeSet);
break;
}
}
}
}
and here is the procedure to validate the user
CREATE PROCEDURE [dbo].[Validate_User]
#Username NCHAR(50),
#Password VARCHAR(50)
AS
BEGIN
SET NOCOUNT ON;
DECLARE #UserId INT, #LastLoginDate DATETIME
SELECT #UserId = UserId, #LastLoginDate = LastLoginDate
FROM NervSuiteUsers
WHERE Username = #UserName AND [Password] = #Password
IF #UserId IS NOT NULL
BEGIN
IF NOT EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName)
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
SELECT #UserName [UserName] -- User Valid
END
ELSE
BEGIN
SELECT -2 -- User not activated.
END
END
ELSE
BEGIN
SELECT -1 -- User invalid.
END
END
The problem is even with a user in the database, I still get "Account not Validated"

In addition to glitches in the SP (already discussed), there are problems in the .NET code, associated with whether the result was an integer (failure) or a string (success). One pragmatic way to resolve this would be to always return the same types. Since the user passes in the username, there's not necessarily a huge point in passing it out again, unless your intent is to auto-correct case insensitive strings, but a simple fix would be to simply select 1 (or some other sentinel value) in the success case, instead of select #UserName.
However, the same problem can be fixed in the existing code, simply by testing the value:
object sqlResult = cmd.ExecuteScalar();
switch (sqlResult)
{
case int i when i == -1:
// TODO ...
break;
case int i when i == -2:
// TODO ...
break;
case string s:
// success, and the value was s
// TODO...
break;
default:
// I HAVE NO CLUE
throw new SomeSensibleException(...);
}
Note this uses "new" C# language syntax features, but the same fundamental approach can also be done manually if you're using down-level C#, via:
if (sqlResult is int)
{
switch ((int)sqlResult)
{
// ...
}
}
else if (sqlResult is string)
{
string s = (string)sqlResult;
// ...
}

Your SP makes contradictory statement to me. Below query will give result only when both username/password matches
SELECT #UserId = UserId, #LastLoginDate = LastLoginDate
FROM NervSuiteUsers
WHERE Username = #UserName AND [Password] = #Password
Then this below query, doesn't make sense
IF #UserId IS NOT NULL // will be true when both username/password matches
BEGIN
IF NOT EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName) // Why this???? This will not be TRUE
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
Thus your else block will gets evaluated and you will get that result you posted
ELSE
BEGIN
SELECT -2 -- User not activated.
END

Apart from all the feedback you have got in comments regarding the issues with the implementation, you have issue with following lines of query.
IF NOT EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName)
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
SELECT #UserName [UserName] -- User Valid
END
ELSE
BEGIN
SELECT -2 -- User not activated.
END
It should not be NOT EXISTS. It should be IF EXISTS because #UserId NOT NULL mean it exists in the table, change your query like following.
IF EXISTS(SELECT UserId FROM NervSuiteUsers WHERE Username = #UserName)
BEGIN
UPDATE NervSuiteUsers
SET LastLoginDate = GETDATE()
WHERE UserId = #UserId
SELECT #UserName [UserName] -- User Valid
END
ELSE
BEGIN
SELECT -2 -- User not activated.
END

Related

System.Data.SqlClient.SqlException: 'Conversion failed when converting the nvarchar value 'STORES' to data type int.'

I am executing a stored procedure in a C# asmx webservice.
the stored procedure is as follows:
#userName NVARCHAR(50),
#password NVARCHAR(50),
#defaultTabApp NVARCHAR(20) OUT
AS
Declare #defaultTabApptemp NVARCHAR(20)
set #defaultTabApptemp ='NoAccess'
BEGIN TRANSACTION
if EXISTS(SELECT Top 1 [userId],[userName] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
Begin
set #defaultTabApptemp = (select Top 1 [dbo].[users].[defaultTabApp] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
end
select #defaultTabApp = #defaultTabApptemp
COMMIT TRANSACTION
return #defaultTabApp
my c# code is:
[WebMethod]
public string Login(string userName, string userPass)
{
string result;
SqlConnection conn = new SqlConnection(new DBConnection().ConnectionString);
try
{
SqlCommand cmd = new SqlCommand("_getSpecificUserLogin", conn);
cmd.CommandType = System.Data.CommandType.StoredProcedure;
if (conn.State == System.Data.ConnectionState.Closed)
{
conn.Open();
}
cmd.Parameters.AddWithValue("#userName", userName);
cmd.Parameters.AddWithValue("#password", userPass);
cmd.Parameters.Add("#defaultTabApp", SqlDbType.NVarChar,20);
cmd.Parameters["#defaultTabApp"].Direction = ParameterDirection.Output;
int i = cmd.ExecuteNonQuery();
result = Convert.ToString(cmd.Parameters["#defaultTabApp"].Value);
}
finally
{
conn.Close();
}
return result;
}
i'm getting the exception found in the title on this line:int i = cmd.ExecuteNonQuery(); I tried to change it to executescalar but had the same problem. I have no int types in my stored procedure. what is the problem exactly? thanks in advance.
The bare bones of your login procedure should look something like the following.
Note, you do not need a transaction, nor do you need to check if anything exists first - that's implied by the fact it assigns a value to your output variable.
Your code doesn't handle the case where the password is incorrect, or if there are no enabled apps for the user.
In reality, you would validate the user first and assign them some sort of ticket to indicate they have logged in successfully and would not be repeatedly checking their password; when it comes time to get their default app, they are already authenticated.
You only need a top 1 if you can have a single user with more than one appAccess='Yes', in which case you are missing the ordering criteria by which it will select the correct one - without a specific order clause, the value is essentially random.
The correct syntax for an output parameter is Output
I would also hope that if this is a public facing application that the password is not plain text and is stored in the database as a hash of the user's password.
create procedure UserLogin
#userName nvarchar(50),
#password nvarchar(50), -- This should be a HASH of the user's password, performed by the application
#defaultTabApp nvarchar(20) output
as
set nocount on
set #defaultTabApp='Default failure message'
select top 1 #defaultTabApp=defaultTabApp
from dbo.Users
where AppAccess=N'Yes'
and Username=#userName and [Password]=#password
order by <<criteria if there are more than 1 matching rows>>
Go
RETURN can only return int. You already used an OUT parameter, you should remove return #defaultTabApp statement at the end of your stored procedure.
Replace
Declare #defaultTabApptemp NVARCHAR(20)
set #defaultTabApptemp ='NoAccess'
BEGIN TRANSACTION
if EXISTS(SELECT Top 1 [userId],[userName] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
Begin
set #defaultTabApptemp = (select Top 1 [dbo].[users].[defaultTabApp] FROM [dbo].[users] WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
end
select #defaultTabApp = #defaultTabApptemp
COMMIT TRANSACTION
return #defaultTabApp
with
Declare #defaultTabApptemp NVARCHAR(20)
select Top 1 #defaultTabApptemp=[dbo].[users].[defaultTabApp] FROM [dbo].[users]
WHERE [userName]=#userName AND [password]=#password AND [AppAccess]=N'Yes')
if #defaultTabApptemp is null SET #defaultTabApptemp ='NoAccess'
set #defaultTabApp = #defaultTabApptemp

Limit user to 10 wrong pin code attempts Web Api #

I have been working on a simple web api that accepts a certain phone number and returns a pin code. The idea is that I want to delete/restrict the user to login if pin code is 10 times wrong. Could it be done in SQL Server or in c# code?
All help is appreciated
Validate in business layer:
public void validate(validateRequest vReq, headers head, ref validateResponse vRes)
{
vRes = new validateResponse();
int isCorrect = vRes.isCorrect;
Data dal = new Data();
dal.OpenConnection();
dal.valpinCode(vReq.pinCode, ref isCorrect);
if (isCorrect == 1)
{
vRes.result.code = 0;
vRes.result.message = "Successful";
vRes.isCorrect = 0;
}
else
{
vRes.result.code = -3;
vRes.result.message = "Access Denied.";
vRes.isCorrect = -1;
}
My valpincode in datalayer:
public void valpinCode(string pinCode, ref int isCorrect)
{
try
{
using (IDbConnection db = new SqlConnection(c.connstr))
{
if (db.State != ConnectionState.Open)
db.Open();
DynamicParameters param = new DynamicParameters();
param.Add("#pincode", pinCode);
param.Add("#exist", dbType: DbType.Int32, direction: ParameterDirection.Output);
con.Execute("valpin", param, commandType: CommandType.StoredProcedure);
isCorrect = param.Get<Int32>("#exist");
if (db.State != ConnectionState.Closed)
db.Close();
}
}
catch (Exception ex)
{
Common log = new Common();
log.LogError(ex);
}
}
My valpin SP:
#pincode nvarchar(50),
#exist bit output
AS
BEGIN
IF NOT EXISTS(
SELECT *
FROM Users
WHERE pinCode=#pincode)
BEGIN
SET #exist=0
END
ELSE
BEGIN
SELECT *
FROM Users
WHERE pinCode=#pincode
SET #exist=1
END
END
Users table:
idUser phone countrycode pincode
1 7006100 961 5716
12 7006107 961 77709
note that the pincode is generated randomly
Execute this script just once to add one column and set its value to zero for all users
alter table users
add invalidAttempts int null
;
update users
set invalidAttempts =0
select * from users
This procedure verifies if user does not exist (-1), if user is blocked(-2) if password is wrong (-3) and user and is is Ok (0)
Any invalid attempt is stored in the new field/column. If it's number reaches 10, the user is blocked. You need to establish another method for unblocking it. If the user is not blocked a valid login will reset invalid attempts.
ALTER PROCEDURE valpin #idUser INT, #pincode NVARCHAR(50)
AS
BEGIN
DECLARE #pin VARCHAR(50)
DECLARE #invalidAtempts INT
SELECT #pin = pincode, #invalidAtempts = invalidAttempts
FROM Users
WHERE idUser = #idUser
IF #invalidAtempts IS NULL
RETURN - 1 --User does not exists
END
IF #invalidAtempts >= 10
RETURN - 2 --blocked
IF #pin = #pincode
BEGIN
UPDATE users
SET invalidAttempts = 0
WHERE idUser = #idUser
RETURN 0 -- OK
END
ELSE
BEGIN
UPDATE users
SET invalidAttempts = invalidAttempts + 1
WHERE idUser = #idUser
RETURN - 3 -- wrong ping
END
You need to modify C# code to reflect this behavior. also, instead of using an output parameter use a return value ParameterDirection.ReturnValue;

Issue in reading stored procedure parameter value

I am unable to read value from stored procedure when executing it.
Here is my stored procedure
ALTER Procedure [dbo].[SaveInvitation]
(
#InvitationID INT OUTPUT,
#UserID INT,
#Email NCHAR(100),
#InvitationGUID UNIQUEIDENTIFIER OUTPUT
)
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
IF #UserID = -1
BEGIN
SET #UserID = NULL
END
IF NOT EXISTS (SELECT 1 FROM Invitations WHERE LTRIM(RTRIM(Email)) = LTRIM(RTRIM(#Email)))
BEGIN
INSERT INTO Invitations (UserID,
Email,
CreateDate)
VALUES (#UserID,
#Email,
GETDATE())
-- GET NEWLY INSERTED INVITATIONS ID
SET #InvitationID = IDENT_CURRENT('Invitations')
-- GET GUID FROM INVITATION ID
SELECT #InvitationGUID = InvitationGUID
FROM Invitations
WHERE InvitationID = #InvitationID
END
ELSE
BEGIN
RAISERROR('ALREADY_INVITATED', 16, 127)
END
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN
ROLLBACK
END
EXEC ThrowError
END CATCH
END
I am executing this procedure from this function
My DataBaseModel.Designer.cs:
public ObjectResult<SaveInvitation_Result> SaveInvitation(ObjectParameter invitationID, Nullable<global::System.Int32> userID, global::System.String email, ObjectParameter invitationGUID)
{
ObjectParameter userIDParameter;
if (userID.HasValue)
{
userIDParameter = new ObjectParameter("UserID", userID);
}
else
{
userIDParameter = new ObjectParameter("UserID", typeof(global::System.Int32));
}
ObjectParameter emailParameter;
if (email != null)
{
emailParameter = new ObjectParameter("Email", email);
}
else
{
emailParameter = new ObjectParameter("Email", typeof(global::System.String));
}
return base.ExecuteFunction<SaveInvitation_Result>("SaveInvitation", invitationID, userIDParameter, emailParameter, invitationGUID);
}
It throws an exception
The data reader is incompatible with the specified 'TestModel.SaveInvitation_Result'. A member of the type, 'InvitationGUID', does not have a corresponding column in the data reader with the same name.
I have created a complex type i.e. SaveUserRegistration_Result and imported one function SaveInvitation of return type SaveUserRegistration_Result.
How can I solve above exception? Is there any change in stored procedure?
Screen shot
It's giving you this error because you're not actually SELECTing back a result. If you added a line to the end of your procedure for example:
SELECT #InvitationGUID AS InvitationGUID
It should work just fine for you.

Catch the error from Stored Procedure in C#

I have a stored procedure that is called to validate a user during login.
If success it returns the user entity, and that works good! My question is if it doesn't work, I'll raise an error in the SP, How do I catch this error and use it in the best way? Right now I'm getting nullrefference, this is the code:
Store procedure:
ALTER PROCEDURE getEmployee
(
#username nvarchar(50),
#password nvarchar(50)
)
AS
DECLARE #Error_MSG nvarchar(50)
BEGIN
IF EXISTS (select * from Employee where eUsername = #username AND pword = #password)
begin
select * from Employee where eUsername = #username AND pword = #password
END
ELSE
BEGIN
SET #Error_MSG = 'Wrong password, or user doesnt exist'
RAISERROR (#Error_MSG, 11,1)
END
END
And in the code it looks like this, the SP is getEmployee
ActivityDatabaseDataContext dc = new ActivityDatabaseDataContext();
Employee emp;
public bool logIn(string piUsername, string piPassword)
{
try
{
emp = dc.getEmployee(piUsername, piPassword).Single();
}
catch (Exception ex)
{
errorMsg = ex.Message + ex.InnerException.Message;
}
if (emp != null)
{
AppHelper.AppHelper.setUser(emp);
return true;
}
else
{
return false;
}
My question is how I should handle the exception?
I wouldn't generally raise an error from a SP unless it was actually a system problem with the operation. Entering the wrong username and password is a user problem, and one you need only deal with at the interface level, so I'd throw most of that SP away and deal with the two use cases (1 row or 0 rows returned) in the business layer or interface code. If 0 rows, throw up the "Wrong username or password" message to the client and if 1, log in.
ALTER PROCEDURE getEmployee
(
#username nvarchar(50),
#password nvarchar(50)
)
AS
BEGIN
select * from Employee where eUsername = #username AND pword = #password
END
Your InnerException is probably null.
You should try to catch and deal with specific exceptions, in this case SqlExceptions.
ALTER PROCEDURE getEmployee
(
#username nvarchar(50),
#password nvarchar(50)
)
AS
BEGIN
select * from Employee where eUsername = #username AND pword = #password
END
...
SqlCommand cmd = new SqlCommand("getEmployee", conn);
cmd.AddWithValue('#username', name);
cmd.AddWithValue('#password', pass);
SqlAdapter da = new SqlAdapter(cmd);
DataSet ds= new DataSet();
da.Fill(ds);
if (ds.Table.Count > 0 && ds.Table.Rows.Count == 1) {
// success
} else {
// fail
}
IF(#Count>0)
BEGIN
SELECT #RetVal = 6
, #ErrMsg = 'A description with the same name exists. Please provide a unique name.'
GOTO ERROR
END
Use the inbuilt StoredProcException in catch, that implies:
catch (StoredProcException spEx)
{
switch (spEx.ReturnValue)
{
case 6:
UserMessageException umEx= new UserMessageException(spEx.Message);
throw umEx;
}
}
You can pass the Message as string instead of spEx.Message

ExecuteNonQuery on a stored proc causes it to be deleted

This is a strange one. I have a Dev SQL Server which has the stored proc on it, and the same stored proc when used with the same code on the UAT DB causes it to delete itself!
Has anyone heard of this behaviour?
SQL Code:
-- Check if user is registered with the system
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.sp_is_valid_user
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.sp_is_valid_user >>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.sp_is_valid_user >>>'
END
go
create procedure dbo.sp_is_valid_user
#username as varchar(20),
#isvalid as int OUTPUT
AS
BEGIN
declare #tmpuser as varchar(20)
select #tmpuser = username from CPUserData where username = #username
if #tmpuser = #username
BEGIN
select #isvalid = 1
END
else
BEGIN
select #isvalid = 0
END
END
GO
Usage example
DECLARE #isvalid int
exec dbo.sp_is_valid_user 'username', #isvalid OUTPUT
SELECT valid = #isvalid
The usage example work all day... when I access it via C# it deletes itself in the UAT SQL DB but not the Dev one!!
C# Code:
public bool IsValidUser(string sUsername, ref string sErrMsg)
{
string sDBConn = ConfigurationSettings.AppSettings["StoredProcDBConnection"];
SqlCommand sqlcmd = new SqlCommand();
SqlDataAdapter sqlAdapter = new SqlDataAdapter();
try
{
SqlConnection conn = new SqlConnection(sDBConn);
sqlcmd.Connection = conn;
conn.Open();
sqlcmd.CommandType = CommandType.StoredProcedure;
sqlcmd.CommandText = "sp_is_valid_user";
// params to pass in
sqlcmd.Parameters.AddWithValue("#username", sUsername);
// param for checking success passed back out
sqlcmd.Parameters.Add("#isvalid", SqlDbType.Int);
sqlcmd.Parameters["#isvalid"].Direction = ParameterDirection.Output;
sqlcmd.ExecuteNonQuery();
int nIsValid = (int)sqlcmd.Parameters["#isvalid"].Value;
if (nIsValid == 1)
{
conn.Close();
sErrMsg = "User Valid";
return true;
}
else
{
conn.Close();
sErrMsg = "Username : " + sUsername + " not found.";
return false;
}
}
catch (Exception e)
{
sErrMsg = "Error :" + e.Source + " msg: " + e.Message;
return false;
}
}
Ok, I have found the answer ... simple when you know how!
I saw this link here :
Disappearing Stored Procedure
Disappearing Stored Procedure
So from the best answer in that I ran :
select syo.name
from syscomments syc
join sysobjects syo on
syo.id = syc.id
where syc.[text] like '%DROP PROC%'
This gave me one of my OTHER stored procs back... sp_is_user_admin, which didn't seem right so I had a quick look ...
create procedure dbo.sp_is_user_admin
#username as varchar(20),
#isadmin as int OUTPUT
AS
BEGIN
declare #profile as varchar(20)
select #profile = profile from CPUserData where username = #username
if #profile = 'admin'
BEGIN
select #isadmin = 1
END
else
BEGIN
select #isadmin = 0
END
END
--*********************************************************************************
-- Check if user is registered with the system
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
BEGIN
DROP PROCEDURE dbo.sp_is_valid_user
IF OBJECT_ID('dbo.sp_is_valid_user') IS NOT NULL
PRINT '<<< FAILED DROPPING PROCEDURE dbo.sp_is_valid_user >>>'
ELSE
PRINT '<<< DROPPED PROCEDURE dbo.sp_is_valid_user >>>'
END
Doh!!! There is the blighter... in the C# what happens is that if the user is valid I also choose what to let them see based on if they are admin or not and calling that was blitzing the sp_is_valid_user proc. Nasty side effect!
// check the user is entitled to use the system at all
if (usrData.IsValidUser(sCurrentUserName, ref sErrMsg))
{
// if the user is admin then let them spoof and edit their own data
if (usrData.UserIsAdmin(sCurrentUserName, ref sErrMsg))
{
chkSpoof.Visible = true;
grdvwUserDataFromDB.Visible = true;
}
}
else
{
// redirect them away
Response.Redirect("UserNotRegistered.aspx");
return;
}
I hope this helps someone else out!
PS: DB Artisan is nasty and if I had the full fat SQL Server available in my Development toolkit then I guess I could have used the profiler to see this being called. ;P I can't install SQL Server 2008 as I don't have the right SP / updates to Visual Studio I think and IT here can't sort it out, annoying!!

Categories