package main import ( "context" "crypto/md5" "encoding/xml" "fmt" "io" "net/http" "net/http/cgi" "strings" "faculty_media_report/api" "faculty_media_report/dbi" "faculty_media_report/pages" ) const cas_url = "https://idp.login.iu.edu/idp/profile/cas/login?service=https://app.law.indiana.edu/faculty/activity/cas" const validation_url_template = "https://idp.login.iu.edu/idp/profile/cas/serviceValidate?ticket=%s&service=https://app.law.indiana.edu/faculty/activity/cas" var xml_declaration = `` type CASAuthenticationSuccess struct { XMLName xml.Name `xml:"authenticationSuccess"` User string `xml:"user"` } type CASResponse struct { XMLName xml.Name `xml:"serviceResponse"` AuthenticationSuccess CASAuthenticationSuccess `xml:"authenticationSuccess"` } func parse_username(response_body []byte) string { var cas_response CASResponse xmlstring := string(response_body) xmlstring = strings.Replace(xmlstring, xml_declaration, "", 1) xmlstring = strings.TrimSpace(xmlstring) raw_xml_data := []byte(xmlstring) e := xml.Unmarshal(raw_xml_data, &cas_response) if e != nil { return "" } return cas_response.AuthenticationSuccess.User } func validate_cas(cas_ticket string) (string, error) { validation_url := fmt.Sprintf(validation_url_template, cas_ticket) username := "" response, e := http.Get(validation_url) if e == nil { response_body, er := io.ReadAll(response.Body) if er != nil { e = er // return error below } else { username = parse_username(response_body) if er != nil { e = er } //if username == "" { // log_to_file(string(response_body)) //} } defer response.Body.Close() } if username != "" { return username, nil } return username, e } func getCas(w http.ResponseWriter, r *http.Request){ fail := func(err_message string) { content := fmt.Sprintf(pages.ErrorPage, err_message) writeHTML(w, content) } cas_ticket := r.URL.Query().Get("ticket") if cas_ticket != "" { // Validate CAS Ticket (and display page if valid) username, e := validate_cas(cas_ticket) username = strings.ToLower(username) if e != nil { content := fmt.Sprintf("CAS validation error: %s", e) fail(content) return } else if username == "" { fail("CAS ERROR - user not found") return } else { db, err := dbi.GetDbConn() if err != nil { fail("Database error") return } defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { fail("Database error") return } defer conn.Close() user, e := dbi.GetUser(conn, username) if e != nil { login_error_message := fmt.Sprintf("User error for user %s: %s", username, e) fail(login_error_message) return } token, e := dbi.GenJWT(user) if e != nil { jwt_error_message := fmt.Sprintf("Login token failure for user %s: %s", username, e) fail(jwt_error_message) return } if user.Status == "admin" { items, e := dbi.GetReportedItems(conn) if e != nil { items_error_message := fmt.Sprintf("Error fetching items: %s", e) fail(items_error_message) return } html, e := pages.DashboardPage(token, items) if e != nil { content := fmt.Sprintf("Error loading dashboard: %s", e) fail(content) return } writeHTML(w, html) return } else if user.Status == "faculty" { writeHTML(w, pages.MainFormPage(token)) return } else { fail("User has invalid status") return } } } else { //No CAS Ticket - validate user http.Redirect(w, r, cas_url, http.StatusFound) } } func writeHTML(w http.ResponseWriter, html string) { w.Header().Set("Content-Type", "text/html; charset=utf-8") fmt.Fprint(w, html) } func handleCas(w http.ResponseWriter, r *http.Request) { } func handleLoginGet(w http.ResponseWriter, r *http.Request) { writeHTML(w, pages.LoginPage("")) } func handleLoginPost(w http.ResponseWriter, r *http.Request) { username := r.FormValue("username") password := r.FormValue("password") fail := func() { writeHTML(w, pages.LoginPage("Username or Password is incorrect.")) } if username == "" || password == "" { fail() return } db, err := dbi.GetDbConn() if err != nil { fail() return } defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { fail() return } defer conn.Close() user, err := dbi.GetUser(conn, username) if err != nil { fail() return } hash := md5.Sum([]byte(password)) if fmt.Sprintf("%x", hash) != user.Password { fail() return } if user.Status == "admin" { token, err := dbi.GenJWT(user) if err != nil { fail() return } items, err := dbi.GetReportedItems(conn) if err != nil { fail() return } html, err := pages.DashboardPage(token, items) if err != nil { fail() return } writeHTML(w, html) return } token, err := dbi.GenJWT(user) if err != nil { fail() return } writeHTML(w, pages.MainFormPage(token)) } func getPortal(w http.ResponseWriter, r *http.Request) { _, e := w.Write([]byte(pages.LandingPage)) if e != nil { content := fmt.Sprintf(pages.ErrorPage, "Unable to display landing page") writeHTML(w, content) } } func main() { mux := http.NewServeMux() mux.HandleFunc("GET /faculty/activity/cas", getCas) mux.HandleFunc("GET /faculty/activity/portal", getPortal) mux.HandleFunc("GET /faculty/activity/login", handleLoginGet) mux.HandleFunc("POST /faculty/activity/login", handleLoginPost) mux.HandleFunc("POST /faculty/activity/api", api.HandleAPI) cgi.Serve(mux) }