7

I have a login form in QGIS and when it is loaded, python gives me an error for, apparently, not recognizing the findchild part. This is due to the fact that the form isn't loaded before the script is executed, so there are no "childs". How can I connect the form so that the user has to be authenticated first in order to access the project?

import psycopg2
import hashlib
from PyQt4.QtCore import *
from PyQt4.QtGui import *

nameField = None
myDialog = None
passField = None

def formOpen(dialog,layerid,featureid):
global myDialog
myDialog = dialog

global nameField
nameField = dialog.findChild(QLineEdit,"username")

global passField
passField = dialog.findChild(QLineEdit,"password")

loginButton = dialog.findChild(QPushButton,"btnLogin")
loginButton.clicked.connect(authenticate)

def authenticate():

conn_string = "host='CORFOU' dbname='addisababa' user='addisababa' password=1"
conn = psycopg2.connect(conn_string)

username = nameField.getText()
hashpassword = hashlib.md5(passField.getText())

cursor = conn.cursor()
cursor.execute('SELECT * FROM users WHERE username=%s AND password=%s', (username, hashpassword))

if cursor.rowcount == 1:
myDialog.accept()
else:
msgBox = QMessageBox()
msgBox.setText("Wrong username or password")
msgBox.exec_()
  • What is the error you are getting? – Nathan W Nov 20 '13 at 13:36
  • Something like "...NoneType doesn't have property clicked..." or something like this. But i figured that out. The script was executed before the form was loaded, so it couldn't find the elements on the form. My question now is, how can I display the login form so the user has to login before he can use the qgs project – GsetIT Nov 20 '13 at 14:04
  • 1
    This question is really old, but I just wanted to point out that this is a really insecure way to do authentication since you're putting all the logic on the client side. You really need a some kind of authentication server for this, if security is important to you at all. – blah238 Jul 31 '15 at 18:05
  • @blah238 Bounty is still unclaimed and time is running out, anyone who can provide something relevant in the form of an answer is likely to get it. – Cameron Sloan Aug 5 '15 at 21:02
  • @CameronSloan thanks but I don't really have much more input than that. Not a QGIS user myself. – blah238 Aug 6 '15 at 0:59
3
+50

Ok without going into server authentication and everything, I think I have a simple solution to generate the form and allow the user to hit it.

I was looking through the code, and it looks pretty incomplete (based off my quick scanning of the Qt info). The reason this code doesn't find anything is because the children (passField, nameField, loginButton) aren't added to the parent element (myDialog).

So, without going into laying things out properly, or creating a friendly user interface form, I put together a little snippet that will create a dialog, add children, and on login button click, find the values in the QLineEdit widgets.

#import modules
import psycopg2
import hashlib
from PyQt4.QtCore import *
from PyQt4.QtGui import *

#setting global strings
userName = ""
userPass = ""

#this function runs when the button is clicked and finds the values
def buttonHandler():
    #get the list of children of the dialog
    children = myDialog.findChildren(QLineEdit)
    #iterate through list and find out user name and password widgets
    for child in children:
        if child.Name == "PassField":
            userPass = child.text()
        elif child.Name == "NameField":
            userName = child.text()
    print "UN: " + userName
    print "PW:" + userPass


#create the global dialog
myDialog = QDialog()

#create a line edit - need to assign a parent so the QLineEdit knows where to go
passField = QLineEdit(myDialog)
passField.Name = "PassField"
#same thing here
nameField = QLineEdit(myDialog)
nameField.Name = "NameField"
#button to login needs a handler when it is clicked
#create the button, assign to the parent, add the click handler
loginButton = QPushButton("Click Me", myDialog)
#click handler
loginButton.clicked.connect(buttonHandler)

That is pretty rough and dirty, and there are numerous ways to make it a lot better. However, I have never played with QT before and didn't want to start a whole learning session for this answer.

Inside the buttonHandler function, you can add a check to whatever you want to authenticate the user name and password combination. This sample only shows controls added to a dialog and how to access them there. From what I read in QT, there are options to layout the form better. One thing to remember, it seems like a lot of these add widgets to collections over and over again. So, unless you declare a QLineEditin the global namespace, you'll need to iterate through the collections looking for a name.

Other things that will help:

For the password QLineEdit, it looks like there is an option that'll allow you to hide text that is entered in there. I didn't experiment with it, but it is another level of security.

In the buttonHandler function, once authenticated, you should be able to fire off some other function or script for your user. In .Net, I use two windows and only show the 2nd one once my user has been authenticated so you may be able to do that. I also store hashes of passwords and compare the hash generated by the user in the login form against the one stored locally. It isn't the most secure, but my users aren't the smartest people and might know how to turn on a computer without help.

Python would be a little trickier since you could invoke the script separately, but you may be able to modify it (like __name__ checking) or somehow provide a better secure way to let people access sensitive items.

It also depends on where you are running the script. I am not sure if you are able to set up a QGIS project to run a script upon opening. If so, this would be decent there so you can either continue to load the project or exit out of it depending on what happens with the user.

A lot of this will depend on your user base. If they are smart enough to outwit your login script and open things independently, or if they turn away at the first sign of resistance, will greatly influence what you're setting up.

2

From going off of what blah238 has mentioned,

This question is really old, but I just wanted to point out that this is a really insecure way to do authentication since you're putting all the logic on the client side. You really need a some kind of authentication server for this, if security is important to you at all.

It may be best to create a specific user at the database level and using this user to connect from QGIS. From here you can use triggers (and other) mechanisms to track changes and this is what databases are designed to do. This looks to be an area that QGIS does not handle the best, but can be done at different levels in the project life to accomplish. Also using a authentication server would be the best way to maintain security.

The site here gives more information on tips to keep your server secure.

The site here gives more information on how to create SQL Server users for additional security.

  • If anyone has any additional information to contribute I would greatly appreciate it! – Cameron Sloan Aug 4 '15 at 17:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.