diff --git a/cert.pem b/cert.pem new file mode 100644 index 0000000000000000000000000000000000000000..3f231b868d6290dbad38d490ed125bc3ad8b9f3a --- /dev/null +++ b/cert.pem @@ -0,0 +1,33 @@ +-----BEGIN CERTIFICATE----- +MIIFxTCCA62gAwIBAgIUSl6GGNRqtWSiCZIILR5cCSSxXVEwDQYJKoZIhvcNAQEL +BQAwcjELMAkGA1UEBhMCY2gxCzAJBgNVBAgMAmdlMQswCQYDVQQHDAJnZTENMAsG +A1UECgwEaHBhYTEOMAwGA1UECwwFaXRpc3QxDTALBgNVBAMMBGFzZGYxGzAZBgkq +hkiG9w0BCQEWDGFzZGZAY29tLmNvbTAeFw0yNDAxMTcxODIxNDhaFw0yNTAxMTYx +ODIxNDhaMHIxCzAJBgNVBAYTAmNoMQswCQYDVQQIDAJnZTELMAkGA1UEBwwCZ2Ux +DTALBgNVBAoMBGhwYWExDjAMBgNVBAsMBWl0aXN0MQ0wCwYDVQQDDARhc2RmMRsw +GQYJKoZIhvcNAQkBFgxhc2RmQGNvbS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC +DwAwggIKAoICAQCcG4n+jSCJ/fU4hPRfauU7nScigQdqfd0uiTO4neEuFOYm27WK +MvIfnWB2/mdysHyX5PtqcOX97ljYY4iZep1OITx4RQUTW5CiLRK/5rddRsroxNjU +lEDeBgEpuAgSt1XcsWS0P9sgGP9JUX08pfG9acd/K0AbqM3oiF52kipBYSfviHbg +oZy/71CjadKrnAh4Y1/DfVbGI5i3/s2y36BiQ/VyF6QWNsm/z+zChthLe/cX2ggm +OcVq9n15wvdZ67SqCUgbY04K8yi5uwq9CWVbBft/tTZOgPt2oqwX1nuLk6+snaiw +2zAkGCf2PG7CXR8JMnNEFTL43MsVZ5kKhAClqE2HjCWEs86k2xFlHA2jVdPteGqr +MXdWrjjad4yChwRN5ZjbNS4ULe6+Rvoreg4aB1UkSLj5cIhd64hHs+LGmwOG1TUm +0x8O2I9DH530ZiQwAC2Imw2uwYSsi4cpoSHuH8Yw3ldJ+Qn/9bZYbQh9gFMhI3LU +JSVRXl1wym79uFic4/Q0iVYZFFDZSJ1I7R/0NH0ExgCqGA2QkBX5COg/37772D/v +GhIadvNJzW/pXXtN+GL6N1Kt//I2Gxz+3qLaIvqEy0xCxm1yUoleqzEz1YpRZf2l +hqNBoxWHxKBw6/WbczKLKRcBGqCcJshR0w7669qSMhgaCfQEv2XZuDvkmwIDAQAB +o1MwUTAdBgNVHQ4EFgQUiSrjLNiP/b9DOKWcwNBPbdCSAo0wHwYDVR0jBBgwFoAU +iSrjLNiP/b9DOKWcwNBPbdCSAo0wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B +AQsFAAOCAgEARW+Qi3WKKr3MgTnXmBMrPkEHrGx0Qe4blYl281wg/uER9VIIa1D+ +mbue0OBx0dHhgDRLMSL1iYtqOprqPFDd99I7skJxOySDJbMX3modNWasTxEdvFsh +4NSWvSHO40fazPfEh8Q5WacBKbcIl8OYeYjKH7552o3KKaZExpBqiu+MHrw4uXLS +LNsXNJnXKKddGPkKNtnaD8Hso6jsvqVHIvL5XbNQtsjSkdDSsoHsaF9uMKQ7DAmi +5QR+mGzVpv8H6mum+clKU9T0Q6EATC5H3kNVOE8p6mc1wiNHLMKeAghzIZr80lml +g+0tge4g1KooHYFGEeEW1gPY8BOxUlTP8q4ghBioR+zZsCH8TRN51Dvgw2ONRlr8 ++LXjQgA4JN96n/r0eI1pGZzGBrvkf4D3GPCeJ29YH421tmprne+3/SaD4/COhXo5 +r+eaSi6kuA7/L5urBY5pffB1Dvmk9lPXKCG/GvEhrPp8p1EMuYjIFxoO0RUymRXh +Bg/D1P6YTFbXiXqk6cpnp6YlKRX+vovyisnAJAADGIDkpeA6jU70wmn4JXR+ZPlx +3/qWLcLU3gX+K+qN9lodKmgTh7+DE8UIAnsQXjYX87LvMrZxG/xYg6PvhkxrrYat +2Z6h3E4iKbvxS816B7pERMwRYjhlNJ/tQMBwmFof1s8Wcjx58nPfyEc= +-----END CERTIFICATE----- diff --git a/go.mod b/go.mod index c6a44a894f149745e0c46265107589aadd61abc6..4d57e9fd6e4a322aae2fb1e922b9250f5d1e17f5 100644 --- a/go.mod +++ b/go.mod @@ -15,6 +15,7 @@ require ( github.com/gorilla/securecookie v1.1.1 // indirect github.com/klauspost/compress v1.13.6 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect + github.com/satori/go.uuid v1.2.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect diff --git a/go.sum b/go.sum index 16c98fd0f4248c0455804ec9f73f32202b934dc7..2adbcc3af40b76822c08a0ec9f5bfe5b0aa7042a 100644 --- a/go.sum +++ b/go.sum @@ -20,6 +20,8 @@ github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/key.pem b/key.pem new file mode 100644 index 0000000000000000000000000000000000000000..78b7c3a0671cf7588929691eea321db8561360a6 --- /dev/null +++ b/key.pem @@ -0,0 +1,52 @@ +-----BEGIN PRIVATE KEY----- +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 +bVXd2leQaQyRmaLmb8ZmC3YC28nGqb0= +-----END PRIVATE KEY----- diff --git a/main.go b/main.go index bf770cf57cf06bf009243391d48ceebe738c66ed..7112cdb2d72a70138f18cd4d6782e6b4dcc753b6 100644 --- a/main.go +++ b/main.go @@ -6,10 +6,12 @@ import ( "fmt" "github.com/go-gomail/gomail" "github.com/joho/godotenv" + uuid "github.com/satori/go.uuid" "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" + "golang.org/x/crypto/bcrypt" "html" "html/template" "io" @@ -32,18 +34,58 @@ var messages []models.Post var err error var user models.User var userUpdated models.User -var username_global string +var usernameGlobal string var globalAvatarURL string = "https://mstd.dansmonorage.blue/system/site_uploads/files/000/000/002/original/mascot.png" var admins []models.User // Session represents a user session. + type Session struct { ID string CreationTime time.Time - UserID int Username string } +var sessionsCollection *mongo.Collection + +func createSession(session Session) error { + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + return err + } + defer client.Disconnect(context.Background()) + + // Get a handle to the sessions collection + sessionsCollection := client.Database("authentication").Collection("sessions") + + // Insert the session into the sessions collection + _, err = sessionsCollection.InsertOne(context.Background(), session) + return err +} +func getSession(sessionID string) (Session, error) { + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + return Session{}, err + } + defer client.Disconnect(context.Background()) + + // Get a handle to the sessions collection + sessionsCollection := client.Database("authentication").Collection("sessions") + + var session Session + err = sessionsCollection.FindOne(context.Background(), bson.M{"id": sessionID}).Decode(&session) + return session, err +} + +func deleteSession(sessionID string) error { + _, err := sessionsCollection.DeleteOne(context.Background(), bson.M{"id": sessionID}) + return err +} + // sessions is a map to store active sessions. var sessions map[string]Session @@ -96,12 +138,13 @@ func getAllUsers() ([]models.User, error) { func adminHandler(w http.ResponseWriter, r *http.Request) { // Check if the user is an admin - cookie, err := r.Cookie("username") - if err != nil { - http.Redirect(w, r, "/signin", http.StatusSeeOther) - return - } - username := cookie.Value + //cookie, err := r.Cookie("username") + //if err != nil { + // http.Redirect(w, r, "/signin", http.StatusSeeOther) + // return + //} + + username := usernameGlobal user, err := getUser(username) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) @@ -199,7 +242,7 @@ func main() { http.HandleFunc("/messages", messagesHandler) http.HandleFunc("/editbyadmin", editbyAdminHandler) http.HandleFunc("/edit", editHandler) - http.HandleFunc("/admin", adminHandler) + http.HandleFunc("/adminDashboard", adminHandler) http.HandleFunc("/delete", deleteHandler) http.HandleFunc("/admin-change-password", adminChangePasswordHandler) http.HandleFunc("/private-messages", privateMessagesHandler) @@ -214,6 +257,7 @@ func main() { http.HandleFunc("/hide-message", hideMessageHandler) http.HandleFunc("/delete-message", deleteMessageHandler) + //log.Fatal(http.ListenAndServeTLS(":5500", "./cert.pem", "./key.pem", nil)) log.Fatal(http.ListenAndServe(":5500", nil)) } func deleteMessageHandler(w http.ResponseWriter, r *http.Request) { @@ -893,8 +937,14 @@ func deleteHandler(w http.ResponseWriter, r *http.Request) { } // getCurrentUser function -func getCurrentUser(r *http.Request) models.User { - return user +func getCurrentUser(username string) (models.User, error) { + // Retrieve the user from the database + user, err := getUser(username) + if err != nil { + return models.User{}, err + } + + return user, nil } // a saveUser function that updates the user and its posts in the database @@ -917,11 +967,32 @@ func saveUser(user models.User) error { } func handlePost(w http.ResponseWriter, r *http.Request) { + // Get the session ID from the cookie + cookie, err := r.Cookie("session_id") + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + } + + sessionID := cookie.Value + + // Retrieve the session from the database + session, err := getSession(sessionID) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + // Get the current user - user := getCurrentUser(r) + user, err := getCurrentUser(session.Username) + // If the user is not found, redirect the user to the sign in page + if err != nil { + http.Redirect(w, r, "/signin", http.StatusSeeOther) + return + + } // Parse the form data - err := r.ParseForm() + err = r.ParseForm() if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return @@ -997,10 +1068,18 @@ func signUpHandler(w http.ResponseWriter, r *http.Request) { panic(err) } //age := r.FormValue("age") + + // Hash the password using bcrypt + hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + user := models.User{ FullName: fullName, Email: email, - Password: password, + Password: string(hashedPassword), Username: username, Age: age, Bio: bio, @@ -1037,12 +1116,38 @@ func successSignin(w http.ResponseWriter, r *http.Request) { return } - cookie := http.Cookie{ - Name: "username", - Value: username_global, - Expires: time.Now().Add(24 * time.Hour), + id := uuid.NewV4() + + // Create a new session object + session := Session{ + ID: id.String(), + Username: r.FormValue("username"), + CreationTime: time.Now(), + } + + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer client.Disconnect(context.Background()) + + // Store the session in the database + err = createSession(session) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return } - http.SetCookie(w, &cookie) + + cookie := &http.Cookie{ + Name: "session_id", + Value: session.ID, + Expires: time.Now().Add(24 * time.Hour), + HttpOnly: true, + } + http.SetCookie(w, cookie) username := cookie.Value fmt.Println("printing username from cookie value") @@ -1057,13 +1162,13 @@ func successSignin(w http.ResponseWriter, r *http.Request) { // Get the username from the database //var user models.User //err = usersCollection.FindOne(context.Background(), bson.M{"username": r.FormValue("username")}).Decode(&user) - err = usersCollection.FindOne(context.Background(), bson.M{"username": username_global}).Decode(&user) + err = usersCollection.FindOne(context.Background(), bson.M{"username": usernameGlobal}).Decode(&user) // Handle form submission for Post Writing messageContent := r.FormValue("message") if messageContent != "" { // Create a new message object message := models.Post{ - Username: username_global, + Username: usernameGlobal, Title: r.FormValue("title"), CreatedAt: time.Now(), Content: messageContent, @@ -1360,15 +1465,24 @@ func getUserMessages(username string) ([]models.Post, error) { func signInHandler(w http.ResponseWriter, r *http.Request) { username := r.FormValue("username") - username_global = username + usernameGlobal = username password := r.FormValue("password") + var user models.User var messages []models.Post err := usersCollection.FindOne(context.TODO(), bson.M{"username": username}).Decode(&user) - if err != nil || user.Username != username || user.Password != password { - http.Redirect(w, r, "/failed-signin", http.StatusTemporaryRedirect) + if err != nil { + http.Error(w, "Invalid username or password", http.StatusUnauthorized) + return + } + + // Compare the hashed password + err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(password)) + if err != nil { + http.Error(w, "Invalid username or password", http.StatusUnauthorized) return } + err = postsCollection.FindOne(context.Background(), bson.M{"username": username}).Decode(&messages) data = models.PageTemplate{ Username: user.Username, @@ -1380,12 +1494,31 @@ func signInHandler(w http.ResponseWriter, r *http.Request) { Messages: messages, } // Create a new session - r.Header.Add("Session-ID", time.Now().String()) + r.Header.Add("session_id", time.Now().String()) + + id := uuid.NewV4() + + // Create a new session object session := Session{ - ID: time.Now().String(), + ID: id.String(), + Username: r.FormValue("username"), CreationTime: time.Now(), } - sessions[session.ID] = session + // Connect to the MongoDB database + clientOptions := options.Client().ApplyURI(URI) + client, err := mongo.Connect(context.Background(), clientOptions) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer client.Disconnect(context.Background()) + + // Store the session in the database + err = createSession(session) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } http.Redirect(w, r, "/profile", http.StatusTemporaryRedirect) } diff --git a/output b/output index e9fb8b51e3ddfdb8c853e26c591530d4d63e32cc..a5727f6b8a05f166432183c4f5ee9ddbbc9e1298 100755 Binary files a/output and b/output differ diff --git a/view/profile.html b/view/profile.html index c57d7ef7d75272ccd456e2a127640cb02056877e..36c7dd44e7f6fb8f82d27f684f8d3055ba0ff7e1 100644 --- a/view/profile.html +++ b/view/profile.html @@ -119,7 +119,7 @@ <a href="/generate-api-token">Generate API Token</a> - <a href="/admin">Admin</a> + <a href="/adminDashboard">Admin</a> </body>