How to verify login permissions using a DataSet and QueryBuilder? - sql

I wasnt quite sure how to properly write the question so bear with me please, here i'll explain as much as I can. And sorry if its too long.
I am building a basic school project that allows employees to register their time-in and time-out of the job to an Access Database, but I am currently struggling with the Login Form.
After many attempts I decided to go the easy way and use both the DataSet designer and the Query Builder from Visual Studio.
This is what I have right now:
An "EmpleadosDataSet" with a "_tblUser" DataTable and its TableAdapter, this adapter uses a query which I've named "GetDataLogin". Heres an image for reference EmpleadosDataSet Design View
What the query does is fill the "_tblUserDataTable" like this:
SELECT [ID EMPLEADO], USUARIO, CONTRASEÑA, PERMISOS
FROM [_tblUser]
WHERE (USUARIO = ?) AND (CONTRASEÑA = ?)
(Notice how I'm not using the PERMISOS at all after selecting it because that´s what I need help with)
For this to work on my code I instantiate my _tblUserDataTable from the EmpleadosDataSet and its TableAdapter:
Private dtLogin As New EmpleadosDataSet._tblUserDataTable
Private taLogin As New EmpleadosDataSetTableAdapters._tblUserTableAdapter
Then I pass both onto a button click event with this:
Private Sub _Button2_Acceder_Click(sender As Object, e As EventArgs) Handles _Button2_Acceder.Click
dtLogin = taLogin.GetDataLogin(_TxtUsuario.Text, _TxtContrasena.Text)
If dtLogin.Count > 0 Then
MsgBox("Bienvenido")
Else
MsgBox("No esta registrado")
End If
End Sub
This works just fine, however, I want to open a different form depending on whether the employee is an "user" or an "admin". This information is stored in the same dataset on a column named "PERMISOS", but I have no clue how to make the query that selects the value from said column and verify it.
My best bet is that I´ll have the query do SELECT PERMISOS and somehow assign that value to a variable that I can then verify with an If statement?
Something with this structure (this is just an example of what I need but I do not know if its the correct procedure, also, value from PERMISOS column is not a variable name or anything, im putting it for reference of what i need to be there):
If **value from PERMISOS column** = "admin" Then
Me.Hide()
Form2_admin.Show()
ElseIf **value from PERMISOS column** = "user" Then
Me.Hide()
Form3_user.Show()
I am not sure if that would be correct at all, bear with me, I havent been able to find how to even do that, assign the value of the column that I already selected with the query to a variable. Every info about this that I find is from people who rather code the whole database connection and queries themselves, haven´t found anything using the designer and query builder that refers to user permissions.
So that's basically what I need help with, how to either use a query or something else to verify user permissions like that, in other words, how to get the -value from PERMISOS column-
And just if clarification is needed, I did also use the Data Source wizard to connect to the Access Database, as it was the easier way, my DataSets are connected to that and filled with the info from it
PS: I understand some of my methods are incorrect, such as storing passwords as plain text, but please understand this is a simple practice about WindowsForms in general, not trying to learn security or others as of right now, will eventually do but right now I just need this.

If you can learn to write some basic ADO.net code, you will be able to get the data you want in the format your want.
First the user interface code. You pass the values in the 2 text boxes to a function and get back the data you want. Notice that the user interface doesn't know or care where the data is coming from. It is completely disconnected from the database.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click, Button2.Click
Dim permission = ValidateEmployee(_TxtUsuario.Text, _TxtContrasena.Text)
If permission = "admin" Then
Me.Hide()
Form2_admin.Show()
ElseIf permission = "user" Then
Me.Hide()
Form3_user.Show()
Else
MessageBox.Show("No esta registrado")
End If
End Sub
The data access code is all in the function and this function doesn't know anything about the user interface. It has not idea that the parameters are coming from text boxes. If you switched this to a web app, you could still use this function; just change where the parameters values are coming from.
The Using blocks ensure that your database objects are closed and disposed even if there is an error.
We can use named parameters with Access but Access pays no attention to the name. For Access (OleDb) these are positional parameters which means that the order that they appear in the sql query must match the order that they are added to the parameters collection (cmd.Parameters.Add)
Private ConStr As String = "Your connection string"
Private Function ValidateEmployee(user As String, pword As String) As String
Dim permissions As String
Using cn As New OleDbConnection(ConStr),
cmd As New OleDbCommand("Select PERMISOS
From _tblUser
Where USUARIO = #User
AND CONTRASEÑA = #Password;", cn)
cmd.Parameters.Add("#User", OleDbType.VarWChar).Value = user
cmd.Parameters.Add("#Password", OleDbType.VarWChar).Value = pword
cn.Open()
Using reader = cmd.ExecuteReader
If reader.HasRows Then
reader.Read()
permissions = reader("PERMISOS").ToString
Else
permissions = ""
End If
End Using
End Using
Return permissions
End Function
The only big problem left is storing passwords as plain text. They should be salted and encrypted to be safe but that is an issue for another day.

Related

How to read an Access database column into an array with VB.NET?

There's already a question like this but its in VBA, and I need VB.NET
I'm trying to get the contents of a column then put them in an array or something so I can then put the data into the items of a ComboBox. If anyone can suggest a better way, id love to know. This is the code I am using right now:
sql = "SELECT Firstname FROM Candidates WHERE Position='President'"
cmd = New OleDbCommand(sql, cn)
dr = cmd.ExecuteReader
So, first lets build the connection to the Access DB.
So, from the menu go project -> "my project name" properties
(last option).
This:
So, in above we hit the [...] button (connection builder).
Then this:
and then:
then use browse to select the Access database file.
If this is a "mdb" file, then we use JET, but for accDB, we have to choose/use
ACE.
So, after selecting the access file, then hit advanced:
this:
Now, it should default to ACE, but MAKE SURE you check this!!!
This:
If you don't see "ACE" in that list, then you not be able to use/open a accDB file, but can use JET to open mdb files.
Keep in mind, that if you are using vs2022?
Then the test connection button will ALWAYS fail, but if you run your project/code, it should work.
Note that you ALSO MUST force your project to x86 if you using/have Access x32 bits.
So, that is this option:
If you don't have/see a x86 option, then use the configeration manager, and create a x86 choice. You MUST do this!!
Ok, so now we have our connection built.
So, on a form, lets drop in a button to "load" the combo box, drop in a combo box.
And lets even drop in a button to show how to get/grab/use a value the user selected.
Now, a combo box can (often) have two columns. one column is "hidden", and the other is for display.
So, in our example, we will fill out the combo box with a list of hotels from the database, and we will use both columns.
"id" - that is the database primary key value - it will be hidden, not display.
"HotelName" that is the column from the database to display in the combo box.
So, in the combo box property sheet, set:
ValueMember to ID
DisplayMember to HotelName (or your column to display).
So, the code for loading the combo box will look like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim strSQL As String =
"SELECT ID, HotelName FROM tblHotelsA ORDER BY HotelName"
cboHotels.DataSource = MyRst(strSQL)
End Sub
Function MyRst(strSQL As String) As DataTable
Dim rstData As New DataTable
Using conn As New OleDbConnection(My.Settings.AccessDB)
Using cmdSQL As New OleDbCommand(strSQL, conn)
conn.Open()
rstData.Load(cmdSQL.ExecuteReader)
End Using
End Using
Return rstData
End Function
And our button code to test get/grab/see the selected value is this:
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Label2.Text = "Hotel Select = " & cboHotels.Text &
" value = " & cboHotels.SelectedValue
End Sub
Note how we are able to get the 2 values fro the combo box (the database primary key "value") and also the text (hotel name).
So, final result is this:

How to edit a record in an access database - visual basic

I want to edit a specific record in an access database but I keep on getting errors
this is the database I want to edit:
Access database
these are flashcards that the user has created and stored in an access database. What I want is that the user is able to edit the difficulty so it appears more/less often
This is the module:
Module Module1
Public Function runSQL(ByVal query As String) As DataTable
Dim connection As New OleDb.OleDbConnection("provider=microsoft.ACE.OLEDB.12.0;Data Source=flashcard login.accdb") 'Establishes connection to database
Dim dt As New DataTable 'Stores database in table called dt
Dim dataadapter As OleDb.OleDbDataAdapter
connection.Open() 'Opens connection
dataadapter = New OleDb.OleDbDataAdapter(query, connection)
dt.Clear() 'Clears datatable
dataadapter.Fill(dt) 'Fills datatable
connection.Close()
Return dt
End Function
End Module
And here is the button that the user can press to edit the database:
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim sql As String
sql = "UPDATE flashcards set difficulty = '" & TxtDifficulty.Text
runSQL(sql)
End Sub
The difficulty column in the database should be able to be edited by the user through the value they entered in txtDifficulty.text
Good to hear I found the problem with the apostrophe.
I am going to need a where statement but the problem I have is that the user can create as much flashcards as they want so how would I write the where statement?
An INSERT statement does not have a WHERE clause but an UPDATE does and is usually by a primary key.
Look at how I add a new record ignoring mHasException and specifically using parameters. In this case a List is used but with little effort an array of DataRow can be passed instead.
Here is an example for updating a record with a DataRow.
To get other code samples for ms-access see the following repository.
In closing, in the above repository I don't get into all possibilities yet there should be enough there to get you going. And when reviewing code I flip between Add for adding a parameter and AddWithValue while Add is the recommend way but both are shown to see differences. see also Add vs AddWithValue.

How to edit a specific entity inside an access database

I made an access database which stores all the flashcards that the user has created.
Here it is:
When the user creates a flashcard, the difficulty is set to 3 and the user is able to change the difficulty through txtDifficulty
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim sql As String
sql = "UPDATE flashcards set difficulty = " & TxtDifficulty.Text 'Changes difficulty to user input
runSQL(sql) 'runs SQL
End Sub
The problem with the above code is that it edits every single flashcard inside the database rather than just the individual flashcard.
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim sql As String
sql = "UPDATE flashcards set difficulty = " & TxtDifficulty.Text 'Changes difficulty to user input
sql = sql & "WHERE Front = " & txtFront.Text
runSQL(sql) 'runs SQL
End Sub
I edited the code to the above which I thought that it will only edit where Front in the database is equal to txtfront.text which is the textbox where the flashcard appears to the user but this just produces an error.
I was wondering how to only edit the specific flashcard that the user is on. Im guessing it might be something to do with the unique identifier in the access database but im not sure how to implement it
Your idea to "only edit where Front in the database is equal to txtfront.text" is a good one, but concatenating values into an SQL query is fraught with potential problems.
If you pass the values to the query as SQL parameters then you don't need to worry about putting quotes around values, which is one of the problems for the code in the question.
You can write the SQL query over more than one line to make it easier to read: extra whitespace in the query is OK, but no whitespace (another of the problems for the code in the question) is a problem, usually a syntax error.
If you keep the database operation in a separate method, it keeps each part of the code in its own method, which makes it easier to be sure that each part is doing what it should and also easier to maintain.
Sub UpdateDifficultyLevel(front As String, difficulty As Integer)
Dim sql = "UPDATE flashcards SET difficulty = ?
WHERE Front = ?"
Using conn As New OleDbConnection("yourConnectionStringHere"),
cmd As New OleDbCommand(sql, conn)
cmd.Parameters.Add("#difficulty", OleDbType.Integer).Value = difficulty
cmd.Parameters.Add("#front", OleDbType.VarWChar).Value = front
conn.Open()
cmd.ExecuteNonQuery()
End Using
End Sub
Private Sub btnSubmit_Click(sender As Object, e As EventArgs) Handles btnSubmit.Click
Dim difficulty As Integer
If Integer.TryParse(TxtDifficulty.Text, difficulty) Then
Dim front = txtFront.Text
UpdateDifficultyLevel(front, difficulty)
Else
' tell user that the difficulty must be a number
End If
End Sub
The parameters are represented by question marks in the query, and you have to add the parameters in the same order as they are used. Giving a name to each parameter ("#front", etc.) is just to make it easier to see which one is which (the name would actually be used if another database, such as SQL Server, was used).
The Using statement makes sure that any "unmanaged resources" are released after they've been used.
You might also need to use the UserID in the update query, but I could not gauge that from the question.

Delete selected rows in datagridview from database

I'm trying to delete the rows which the user selects in a datagridview using SQL Server.
I have a datagridview that loads the contents of the database, in this case Username and Password.
Keep in mind that each username is unique in the database and so I should be able to delete each row using the unique username.
The SQL I currently have is DELETE FROM 'Users&Passwords' WHERE 'Username' = ? I'm not sure if this is entirely correct, however whenever I click QueryBuilder it seems to accept it.
The code I have to try and do this is as follows:
Private Sub btn_remove_Click(sender As Object, e As EventArgs) Handles btn_remove.Click
For Each row As DataGridViewRow In DataGridView1.SelectedRows
Dim usernametodelete As String = DataGridView1.SelectedRows(0).Cells(0).Value.ToString
'TextBox1.Text += usernametodelete
Me.Users_PasswordsTableAdapter.DeleteUser(usernametodelete)
Me.Users_PasswordsTableAdapter.Fill(Me.ManageUsersDataSet._Users_Passwords)
Next
End Sub
I would like that when the user clicks the Remove User(s) button the selected rows from the datagridview remove the rows from the database. Any help would be greatly appreciated.
You're getting slightly more involved than you need to. The set of steps you'd ideally take would be:
0) rename your table so it doesn't have an & character in it - it's just asking for trouble
1) Add your table to your dataset with something like this process: right click the design surface, new tableadapter, configure the connection string, set the query as SELECT * FROM Users WHERE Username LIKE #username, ensure other things are set, like whether you want generate dbdirect methods, and whether you want the dataset to refresh new values
2) In your data sources window (might not be showing by default, find it in on of visual studio's menus) drag and drop the Users node from the data soruces window, out onto the form. This will create a datagridview bound to the typed datatable, and also create a dataset, tableadapter, tableadaptermanager (not strictly needed; can delete), a bindingnavigator tool strip (again not strictly needed but has a handy pre-wired Save button on it) and a bindingsource. It will also pre-fill some code into the form_load event handler to fill the datatable
3) That's it - your form has a grid, bound to a datatable. That grid is capable of deleting rows - click the row header and press the delete button on the keyboard, row disappears. Click the save icon in the toolstrip, and the change is persisted to the database. The only thing you have to do is get data into the table in the first place. I gave a SQL that allows you to choose some usernames to load, but you can easily make it load the whole lot by changing the line of code in Form_Load to:
yourTableAdapterName.Fill(yourDatasetname.YourDatatableName, "%")
Passing a percent as the name is like a * wildcard in DOS. LIKE % will select all records. You can also, of course, leave the default provision (it will reference a textbox on the toolstrip) and instead run the program, put a % in the textbox on the toolstrip and click Fill. Or you can put JOHNSMITH, or JOHN% in th textbox and fill, to load that user/those users respectively
Hopefully you already did 1 and 2..
A bit of a background story on how all this stuff works:
DataTables are client side representations of tables in the database. It isn't intended that they contain all the data in the database, only a portion of this that you want to work with. When you load a row from the DB you can edit it, delete it (mark as deleted) and when you call tableAdapter.Update(yourtable) th changes you made are persisted to the DB. Note that even though it's called Update, the method will save new rows (by doing an INSERT), deleted rows (SQL DELETE) and make updates (SQL UPDATE). If your datatable has 4 rows laodd from the DB, and you then add 2 new rows, make changes to 3 of the loaded rows and delete the 4th row, then calling tableadapter.Update will save all these changes to the DB. You aren't required to pick through your datatable, calling tableadapter.insert/update/delete accordingly, to manually persist these changes. The flow of using a tableadapter is thus:
tableAdapter.Fill(datatable, maybe,parameters,to,control,the,fill,here)
'work with the datatable here, change, insert, delete. Use loops/code, use the UI, etc
tableAdapter.Update(datatable) 'save changes
I suggest putting an id column in your datagridview and just hide it so that you can easily delete the record you want to remove easily without conflict, because if you will use a username, what if theres a duplicate username exist.
Private Sub BTNDELETE_Click(sender As Object, e As EventArgs) Handles BTNDELETE.Click
Public result As Integer = 0
Public cmd As New MySqlCommand
strcon.Open() 'your connection string here
With cmd
.Connection = strcon 'your connection string
.CommandText = "DELETE FROM `users` WHERE `id` = '" & DTGLIST.CurrentRow.Cells(0).Value.ToString() & "';"
result = cmd.ExecuteNonQuery
If result > 0 Then
MsgBox("Successfully Removed.", MsgBoxStyle.Information, "Info")
DTGLIST.Rows.Remove(DTGLIST.SelectedRows(0))
End If
End With
strcon.Close() 'close the connection string
End Sub
Managed to figure out why my delete query wasn't working a few days ago. It was due to my SQL not being correct. This worked for me DELETE FROM [Users&Passwords] WHERE (Username = ?).
Also having the line Me.Users_PasswordsTableAdapter.DeleteUser(usernametodelete) in my code made it work.

Retrieving ##IDENTITY in AccessDB

I'm trying to retrieve the ##IDENTITY value from an access database after I add a row to it. However, instead of using hard coded connections, I'm using the DataSet wizard. Unfortunately, due to the Jet engine, I can't do more than one command at a time. I tried to select the ##IDENTITY in a separate command but unfortunately, I guess it counts as a different command as it returns a 0 each time.
My question is, is there a way I can use the GUI/IDE to retrieve the ##IDENTITY or do I have hard code the connection, command, query values and obtain the value that way.
Thanks.
You asked an interesting questions which I did not know the answer to. After some research I found this and it seems promising. However I have never used it and cannot substantiate if and how well it works.
I also don't know the DataSet wizard that well, but vaguely recalls that it generates an OleDbAdapter object, hopefully it exposes a RowUpdated event which you can hook this code to.
I have pasted the interesting MSDN code parts here:
(Link to full documentation)
' Create the INSERT command for the new category.
adapter.InsertCommand = New OleDbCommand( _
"INSERT INTO Categories (CategoryName) Values(?)", connection)
adapter.InsertCommand.CommandType = CommandType.Text
Then hook-up and event listener to RowUpdated
' Include an event to fill in the Autonumber value.
AddHandler adapter.RowUpdated, _
New OleDbRowUpdatedEventHandler(AddressOf OnRowUpdated)
Obtain the ##Identity with the same connection.
Private Shared Sub OnRowUpdated( _
ByVal sender As Object, ByVal e As OleDbRowUpdatedEventArgs)
' Conditionally execute this code block on inserts only.
If e.StatementType = StatementType.Insert Then
' Retrieve the Autonumber and store it in the CategoryID column.
Dim cmdNewID As New OleDbCommand("SELECT ##IDENTITY", _
connection)
e.Row("CategoryID") = CInt(cmdNewID.ExecuteScalar)
e.Status = UpdateStatus.SkipCurrentRow
End If
End Sub
Hopefully this helps you out or least someone will set me straight :)