Raspberry BASIC > Programming Challenges

GUI Login

<< < (3/4) > >>

John Spikowski:
I wanted to try Nim / IUP on Windows 10 and I was able to get the login.nim to compile. I seem to be having a problem getting the image to load.

John Spikowski:
I had a little better luck with ScriptBasic and IUP. I still can't get the image to  show even by making a .bmp file of it. The IUP I'm running is pretty old so I'm  going to update to 3.28 and see if that helps.

John Spikowski:
It turns out my .bmp image needed to be 24 bit color depth. The conversion from .png to .bmp didn't maintain the transparency of the background.

I also learned how to create Windows executables rather than default console apps.

nim --app:gui c login.nim

I'm going to start a thread on AllBASIC for the Windows / Nim / IUP direction. Maybe Mike will show some interest.



John Spikowski:
ScriptBasic Update

This version adds Jalih's suggestion of maintaining a database of UserID and Password (encrypted) as an extension to the base challenge.

Change Log
[*] Added check for duplicate UserID's. 12/31
[*] Fixed MD5 hex string to be a constant 32 bytes. 12/31
[*] Reset error message to login error after a duplicate user add. 1/1
[/list]


--- Code: Script BASIC ---' ScriptBasic / IUP - Login - JRS IMPORT iup.basIMPORT sqlite.basDECLARE SUB MD5 ALIAS "md5fun" LIB "t" initdb = FILELEN("user.db")usrdb = sqlite::open("user.db")IF initdb = 0 THEN  sqlite::execute(usrdb,"CREATE TABLE user (" & _    "UserID VARCHAR NOT NULL, " & _    "Password VARCHAR NOT NULL, " & _    "CONSTRAINT user_pk PRIMARY KEY (UserID));")END IF SUB login_clicked  uid = Iup::GetAttribute(uidtxt,"VALUE")  IF Iup::GetAttribute(showpwd, "VALUE") = "OFF" THEN    pwd = Iup::GetAttribute(pwdtxt1,"VALUE")  ELSE    pwd = Iup::GetAttribute(pwdtxt2,"VALUE")  END IF  ' *** NEW USER ***  IF newusr_cb = "ON" THEN    IF uid <> "" AND pwd <> "" THEN      rtnmsg = sqlite::execute(usrdb,"INSERT INTO user VALUES ('" & uid & "', '" & MD5STR(pwd) & "');")      IF rtnmsg = 19 THEN        Iup::SetAttribute errlbl, "TITLE", "Duplicate User ID"        Iup::Show errlbl        Iup::SetFocus uidtxt        EXIT SUB       END IF      Iup::Message "Login",  "User Added"      GOSUB Init_Form      EXIT SUB    ELSE      Iup::Show errlbl      Iup::SetFocus uidtxt      EXIT SUB     END IF  END IF  ' *** LOGIN VERIFY ***  Iup::SetAttribute errlbl, "TITLE", "User ID / Password Incorrect"  stmt = sqlite::query(usrdb,"SELECT Password FROM user WHERE UserID = '" & uid & "';")  IF sqlite::row(stmt) = sqlite::SQLITE3_ROW THEN    sqlite::fetchhash(stmt, col)    IF col{"Password"} = MD5STR(pwd) THEN      Iup::Hide errlbl      Iup::Message "Login",  "Login Successful"      GOSUB Init_Form      EXIT SUB    END IF  END IF  Iup::Show errlbl  Iup::SetFocus uidtxt  EXIT SUB  Init_Form:    Iup::Hide errlbl     Iup::SetAttribute uidtxt, "VALUE", ""     Iup::SetAttribute pwdtxt1, "VALUE", ""    Iup::SetAttribute pwdtxt2, "VALUE", ""    Iup::SetAttribute showpwd, "VALUE", "OFF"    Iup::SetAttribute newusr, "VALUE", "OFF"    Iup::SetAttribute zbtxt, "VALUE", "pwdtxt1"    Iup::SetAttribute loginbut, "TITLE", "Login"    newusr_cb = "OFF"    Iup::SetFocus uidtxt  RETURNEND SUB FUNCTION MD5STR(txtstr)  LOCAL tmpstr, hexstr, x  tmpstr = MD5(txtstr)  FOR x = 1 TO 16    hexstr &= RIGHT("0" & HEX(ASC(MID(tmpstr, x, 1))), 2)  NEXT  MD5STR = hexstrEND FUNCTION SUB show_password  IF Iup::GetAttribute(showpwd, "VALUE") = "OFF" THEN    pwd2 = Iup::GetAttribute(pwdtxt2,"VALUE")     Iup::SetAttribute pwdtxt1, "VALUE", pwd2    Iup::SetAttribute zbtxt, "VALUE", "pwdtxt1"  ELSE    pwd1 = Iup::GetAttribute(pwdtxt1,"VALUE")     Iup::SetAttribute pwdtxt2, "VALUE", pwd1    Iup::SetAttribute zbtxt, "VALUE", "pwdtxt2"  END IFEND SUB SUB new_user  newusr_cb =Iup::GetAttribute(newusr, "VALUE")  IF newusr_cb = "ON" THEN    Iup::SetAttribute loginbut, "TITLE", "Add User"  ELSE    Iup::SetAttribute loginbut, "TITLE", "Login"  END IFEND SUB   SUB Win_exit  Iup::ExitLoop = TRUEEND SUB ' *** DIALOG ***Iup::Opendlg = Iup::Create("dialog")Iup::SetAttributes dlg, _  "TITLE=\"Login\", " & _  "SIZE=200x200, " & _  "MAXBOX=NO, " & _  "MINBOX=NO, " & _  "RESIZE=NO, " & _  "DEFAULTENTER=\"loginbut\"" ' *** CONTAINER ***vb = Iup::Create("vbox") ' *** IMAGE ***hb1 = Iup::Create("hbox")piclbl = Iup::Create("label")Iup::SetAttributes piclbl, _  "IMAGE=\"./login.png\", " & _  "EXPAND=HORIZONTAL, " & _  "ALIGNMENT=ACENTER:ATOP"Iup::Append hb1, piclblIup::Append vb, hb1 ' *** ERROR ***hb2 = Iup::Create("hbox")errlbl = Iup::Create("label")Iup::SetAttributes errlbl, _  "TITLE=\"User ID / Password Incorrect\", " & _  "FGCOLOR=\"#ff0000\", " & _  "EXPAND=HORIZONTAL, " & _  "ALIGNMENT=ACENTER"Iup::Hide errlblIup::Append hb2, errlblIup::Append vb, hb2 ' *** ENTRY ***hb3 = Iup::Create("hbox")Iup::SetAttributes hb3, _   "MARGIN=20x10, " & _  "GAP=5"vb1 = Iup::Create("vbox")Iup::SetAttribute vb1, "GAP", "15"uidlbl = Iup::Create("label")Iup::SetAttribute uidlbl, "TITLE", "User ID"Iup::Append vb1, uidlblpwdlbl = Iup::Create("label")Iup::SetAttribute pwdlbl, "TITLE", "Password"Iup::Append vb1, pwdlblIup::Append hb3, vb1vb2 = Iup::Create("vbox")uidtxt = Iup::Create("text")Iup::SetAttribute uidtxt, "SIZE", "85x"Iup::Append vb2, uidtxtpwdtxt1 = Iup::Create("text")Iup::SetAttributes pwdtxt1, _  "PASSWORD=YES, " & _  "SIZE=85x"pwdtxt2 = Iup::Create("text")Iup::SetAttribute pwdtxt2, "SIZE", "85x"Iup::SetHandle "pwdtxt1", pwdtxt1Iup::SetHandle "pwdtxt2", pwdtxt2zbtxt = Iup::Zbox(pwdtxt1, pwdtxt2)Iup::SetHandle "zbtxt", zbtxtIup::Append vb2, zbtxtIup::Append hb3, vb2Iup::Append vb, hb3 ' *** SHOW/HIDE ***hb4 = Iup::Create("hbox")Iup::SetAttributes hb4, _ "MARGIN=35x, " & _ "GAP=40"showpwd = Iup::Create("toggle")Iup::SetAttributes showpwd, _  "TITLE=\"Show Password\", " & _  "CANFOCUS=NO"newusr = Iup::Create("toggle")Iup::SetAttributes newusr, _  "TITLE=\"New User\", " & _  "CANFOCUS=NO"Iup::Append hb4, showpwdIup::Append hb4, newusrIup::Append vb, hb4 ' *** LOGIN ***hb5 = Iup::Create("hbox")Iup::SetAttribute hb5, "MARGIN", "40x10"loginbut = Iup::Create("button")Iup::SetAttributes loginbut, _  "TITLE=\"Login\", " & _  "EXPAND=HORIZONTAL"Iup::SetHandle "loginbut", loginbutiup::Append hb5, loginbutiup::Append vb, hb5 ' *** ATTACH CONTAINER ***iup::Append  dlg, vb ' *** CALLBACKS ***Iup::SetCallback dlg,"CLOSE_CB",ADDRESS(Win_exit())Iup::SetCallback loginbut, "ACTION", ADDRESS(login_clicked())Iup::SetCallback showpwd, "ACTION", ADDRESS(show_password())Iup::SetCallback newusr, "ACTION", ADDRESS(new_user()) ' *** PROCESS ***' Iup::Show(Iup::LayoutDialog(dlg))Iup::Show(dlg)Iup::MainLoopIup::Closesqlite::Close(usrdb) 

ubuntu@rpi4b:~/guilc/sb-dev$ scriba showdb.sb
John  -  FAF43E604E6A820CAAAB88BF7E2328C7
AIR   -  99B69173F38EB9F24FA4C05B4BE47CD7
Jalih -  78020D21C627349463D88142DC51F9B9
Peter -  0C5213856A47300D1E6D30CA00FAFFC1
ubuntu@rpi4b:~/guilc/sb-dev$



jalih:
Here is a updated 8th login dialog with database support for retrieving stored user data.

I currently generate 32 byte random buffer from the cryptographically strong random source and convert it to hex string for the salt. Key for user chosen password is generated with PBKDF2 algorithm using previously randomly generated salt and 10000 iterations as parameters. Username, key and salt is then stored into database.


--- Code: ---requires gui

var userdb

defer: auth

{
  kind: "edit",
  bounds: "edit1.left, lbl2.top, parent.width-20, top+24",
  name: "edit2",
  max-text: 32,
  password-char: "*",
  return-pressed: ' auth ,
  text-changed: ( "lbl0" g:child "" g:text drop )
} g:new constant edit2-hide-passwd

{
  kind: "edit",
  bounds: "edit1.left, lbl2.top, parent.width-20, top+24",
  name: "edit2",
  max-text: 32,
  password-char: "",
  return-pressed: ' auth ,
  text-changed: ( "lbl0" g:child "" g:text drop )
} g:new constant edit2-show-passwd

: alternator \ a -- a[0]
  a:shift dup >r
  a:push drop r> ;

[ ` edit2-show-passwd ` , ` edit2-hide-passwd ` ] ' alternator curry: next-state

\ auth states
0 constant NOT-FOUND
1 constant USER-PASSWD-MATCH
2 constant USER-PASSWD-FAIL

: auth-from-db \ id passwd -- state
  s:len 0 n:= if
    2drop
    USER-PASSWD-FAIL
    ;;
  then
  >r >r
  userdb @ "by-id" r> 1 a:close db:bind-exec[]
  a:len 0 n:> if
    a:open
    "salt" m:@ r> swap 10000 cr:genkey
    swap "key" m:@ nip b:= if
      USER-PASSWD-MATCH
    else
      USER-PASSWD-FAIL
    then
  else
    drop
    rdrop
    NOT-FOUND
  then
  nip ;

: authenticate
  "edit2" g:child g:text? >r
  "edit1" g:child g:text? r> auth-from-db

   [ ( "lbl0" g:child "User not found!" g:text ) ,
     ( "Authenticated!" . cr bye ) ,
     ( "lbl0" g:child "User and password don't match!" g:text )  ] swap
   caseof drop ;

' authenticate w:is auth

: init
  "mytest.db" f:slurp "No database" thrownull
  db:open
  \ make a prepared statement
  "SELECT * FROM user WHERE id=? LIMIT 1" "by-id" db:prep-name
  userdb ! "edit1" g:child g:focus  ;

{
  kind: "win",
  buttons: 5,
  native-title-bar: false,
  title: "Login",
  wide: 520,
  high: 220,
  resizable: false,
  center: true,
  init: ' init ,
  children:
  [
    {
      kind: "box",
      name: "frame",
      bounds: "0, 0, parent.width, parent.height",
      bg: "gray",
      children:
      [
        {
          kind: "image",
          bounds: "parent.left+10, parent.top+10, left+128, top+128",
          img: "8thlogo.png",
          name: "logo"
        },
        {
          kind: "label",
          fg: "red",
          font: 20,
          label: "",
          bounds: "logo.right+20, parent.top+10, parent.width-10, top+24 ",
          justify: ["hcenter"],
          name: "lbl0"
        },
        {
          kind: "label",
          label: "Username:",
          bounds: "logo.right+20, lbl0.bottom+20, left+80, top+24 ",
          name: "lbl1"
        },
        {
          kind: "edit",
          bounds: "lbl1.right+10, lbl1.top, parent.width-20, top+24",
          name: "edit1",
          max-text: 32,
          return-pressed: ( "edit2" g:child g:focus drop ),
          text-changed: ( "lbl0" g:child "" g:text drop )
        },
        {
          kind: "label",
          label: "Password:",
          bounds: "lbl1.left, lbl1.bottom+10, left+80, top+24",
          name: "lbl2"
        },
        {
          kind: "edit",
          bounds: "edit1.left, lbl2.top, parent.width-20, top+24",
          name: "edit2",
          max-text: 32,
          password-char: "*",
          return-pressed: ' authenticate ,
          text-changed: ( "lbl0" g:child "" g:text drop )
        },
        {
          kind: "toggle",
          label: "Show password",
          adjustwidth: true,
          bounds: "edit1.left, lbl2.bottom+20, left+100, top+24",
          name: "toggle",
          click: ( "edit2" g:child g:text? >r "frame" g:child "edit2" g:-child next-state g:+child "edit2" g:child r> g:text drop )
        },
        {
          kind: "btn",
          label: "Login",
          bg: "darkgray",
          bounds: "lbl2.left, lbl2.bottom+60, edit1.right, top+30",
          name: "button",
          tooltip: "Login to account",
          click: ' authenticate
        }
      ]
    }
  ]
} g:new var, gui

: app:main
  ( userdb @ db:close ) onexit ;

--- End code ---

I currently manually generate user database with the following piece of 8th code:

--- Code: ---"Creating a database and adding data:\n" .

\ remove previous db:
"mytest.db" f:rm

\ open a database:
"mytest.db" db:open "Could not create database" thrownull nip

\ create a table
"CREATE TABLE user (id PRIMARY KEY, key, salt)" db:exec

\ make a prepared statement
"INSERT INTO user VALUES (?, ?, ?);" "insert" db:prep-name

: build-params  \ id passwd -- a
  cr:uuid >r
  r@ 10000 cr:genkey
  r>
  3 a:close ;

\ insert into table using a parameterized statement
\ You could use t:err? to get map of error information
"insert" "jalih" "pa$$w0rd!" build-params null db:bind-exec
"insert" "guest" "guest" build-params null db:bind-exec
"insert" "john" "hard-passwd" build-params null db:bind-exec
"insert" "air" "crack-this" build-params null db:bind-exec

\ and close the database
"All done!\n" .
db:close
bye

--- End code ---

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version