Wednesday, 4 November 2020

go web app 16 authentication mongodb

 
index page, not logged in

try logging in, but user not exist

create a new account

new account created, redirect to log in

register duplicate driver license will be rejected.
server search mongodb first, only add new user

user added to mongodb

logged in, sign out

sign out page, view ballots redirect to index page

logged out

//tutorial.go

package main

import (
"context"
"fmt"
"html/template"
"log"
"net/http"
"time"

"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"golang.org/x/crypto/bcrypt"

"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)

type auth struct {
Username string
Password []byte
Status   string
}

var templates *template.Template
var store = sessions.NewCookieStore([]byte("secret"))

func indexGetHandler(w http.ResponseWriter, r *http.Request) {

session, _ := store.Get(r, "session")
registeredUser, _ := session.Values["username"]
registeredHashedPassword, _ := session.Values["password"]
status, _ := session.Values["status"]

if registeredUser == nil || registeredHashedPassword == nil || status == nil {
templates.ExecuteTemplate(w, "index.html", nil)
return
}

templates.ExecuteTemplate(w, "index.html",
auth{Username: registeredUser.(string),
Password: registeredHashedPassword.([]byte), Status: status.(string)})
}

func loginGetHandler(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session")
registeredUser, _ := session.Values["username"]
//registeredHashedPassword, _ := session.Values["password"]
status, _ := session.Values["status"]

if registeredUser == nil || status == nil {
templates.ExecuteTemplate(w, "login.html", nil)
return
}

templates.ExecuteTemplate(w, "login.html",
auth{Username: registeredUser.(string),
Status: status.(string)})

}

func loginPostHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.PostForm.Get("username")
password := r.PostForm.Get("password")

var userAuth auth
userAuth = findUser(username)
if userAuth.Status != "voter found" {
templates.ExecuteTemplate(w, "login.html",
auth{Status: "voter not exist"})
return
}

err := bcrypt.CompareHashAndPassword(userAuth.Password, []byte(password))

if err != nil || username != userAuth.Username {
templates.ExecuteTemplate(w, "login.html",
auth{Status: "username password mismatch"})
return
}

session, _ := store.Get(r, "session")
session.Values["username"] = username
session.Values["password"] = userAuth.Password
session.Values["status"] = "logged in as " + username
session.Save(r, w)

http.Redirect(w, r, "/", 302)

}

func logoutGetHandler(w http.ResponseWriter, r *http.Request) {
session, _ := store.Get(r, "session")
session.Values["username"] = nil
session.Values["password"] = nil
session.Values["status"] = nil
session.Save(r, w)

templates.ExecuteTemplate(w, "logout.html", nil)
}

func registerGetHandler(w http.ResponseWriter, r *http.Request) {
templates.ExecuteTemplate(w, "register.html", nil)
}

func registerPostHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.PostForm.Get("username")
password := r.PostForm.Get("password")

cost := bcrypt.DefaultCost
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), cost)
if err != nil {
templates.ExecuteTemplate(w, "register.html", auth{Status: "password not accepted"})
return
}

status := addUser(username, hashedPassword)

session, _ := store.Get(r, "session")
        session.Values["username"] = username
session.Values["status"] = status
session.Save(r, w)

http.Redirect(w, r, "/login", 302)
}

func addUser(user string, password []byte) string {
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb+srv://bob:password@cluster0.yvyo2.mongodb.net/test?retryWrites=true&w=majority"))
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), 100*time.Second)
err = client.Connect(ctx)
if err != nil {
return "error connecting to database"
}
defer client.Disconnect(ctx)

electionDatabase := client.Database("sample_election")
votersCollection := electionDatabase.Collection("voters")

//check if voter exist
var userAuth auth
userAuth = findUser(user)
if userAuth.Status == "voter found" {
return "You are registered already, please log in."
}

address, err := votersCollection.InsertOne(ctx, bson.D{
{"user", user},
{"password", password},
})
if err != nil {
return "error adding new voter"
}

fmt.Println("inserted", user, "@ ", address)
return "new volter profile created"
}

func findUser(user string) auth {
client, err := mongo.NewClient(options.Client().ApplyURI("mongodb+srv://bob:password@cluster0.yvyo2.mongodb.net/test?retryWrites=true&w=majority"))
if err != nil {
log.Fatal(err)
}
ctx, _ := context.WithTimeout(context.Background(), 100*time.Second)
err = client.Connect(ctx)
if err != nil {
return auth{Status: "error connecting to database"}
}
defer client.Disconnect(ctx)

electionDatabase := client.Database("sample_election")
votersCollection := electionDatabase.Collection("voters")

filterCursor, err := votersCollection.Find(ctx, bson.M{"user": user})
defer filterCursor.Close(ctx)
if err != nil {
return auth{Status: "voter not found"}
}

var userAuth bson.M
var userLiscense string
var userPassword []byte

filterCursor.Next(ctx)
if err = filterCursor.Decode(&userAuth); err != nil {
return auth{Status: "error connecting to database 2"}
}

for k, v := range userAuth {
if k == "user" {
userLiscense = v.(string)
}
if k == "password" {
userPassword = v.(primitive.Binary).Data
//fmt.Println(reflect.TypeOf(v))
}
}

return auth{Username: userLiscense, Password: userPassword, Status: "voter found"}
}

func main() {
templates = template.Must(template.ParseGlob("templates/*.html"))
r := mux.NewRouter()
r.HandleFunc("/", indexGetHandler).Methods("GET")
//r.HandleFunc("/", indexPostHandler).Methods("POST")
r.HandleFunc("/login", loginGetHandler).Methods("GET")
r.HandleFunc("/login", loginPostHandler).Methods("POST")
r.HandleFunc("/logout", logoutGetHandler).Methods("GET")
r.HandleFunc("/register", registerGetHandler).Methods("GET")
r.HandleFunc("/register", registerPostHandler).Methods("POST")
fs := http.FileServer(http.Dir("./static/"))
r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", fs))
http.Handle("/", r)
http.ListenAndServe(":8000", nil)
}

------------------------------------
//index.html
<html>

<head>
    <title> Templates</title>
    <link rel="stylesheet" type="text/css" href="/static/index.css">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>

<body>
    <div style="text-align: center;">
        <h1>World Election of Year 3000 </h1>
    </div>

    {{if .Username}}
    <div>I ({{.Username}}) vote for</div>
    <div><a href="/logout">sign out</a></div>
    {{else}}
    <div>Please <a href="/login">log in</a></div>
    {{end}}
    <!--
    <form method="POST">
        <textarea name="textarea1"></textarea>
        <div>
            <button type="submit">Post Comment</button>
        </div>
    </form>
    -->
    <div>status: {{.Status}}</div><br />
</body>

</html>

-------------------------------
//login.html
<html>

<head>
    <title>Login</title>
</head>

<body>
    <h1>Log In</h1>
    <form method="POST">
        <div> Driver's Liscense: <input name="username"></div><br />
        <div></div>Password: <input name="password"></div><br />
        <div> <a href="/register">create new account</a></div><br /><br />
        <div> <button type="submit">Login</button></div>
    </form>
    <div>status: {{.Status}}</div><br />
</body>

</html>

---------------------------------
//register.html
<html>

<head>
    <title>Register</title>
</head>

<body>
    <h1>Registration</h1>
    <form method="POST">
        <div> Driver's Liscense: <input name="username"></div><br />
        <div><input type="button" onclick="inputClick(); this.value=' &#10004'" value='voter validation'></input>
        </div><br />
        <div></div>Password: <input name="password"></div><br /><br />
        <div> <button type="submit" id="button1" disabled>Register</button></div><br />
    </form>
    <div>status: {{.Status}}</div><br />
</body>

<script>
    function inputClick() {
        document.getElementById('button1').disabled = false;
    }
</script>

</html>

reference:

mongodb insert

mongodb query

No comments:

Post a Comment