diff --git a/api/api.go b/api/api.go index 7266ddb..318c526 100644 --- a/api/api.go +++ b/api/api.go @@ -54,14 +54,14 @@ func HandleAPI(w http.ResponseWriter, r *http.Request) { db, err := dbi.GetDbConn() if err != nil { - http.Error(w, "internal error", http.StatusInternalServerError) + http.Error(w, "internal error - error getting db connection", http.StatusInternalServerError) return } defer db.Close() conn, err := db.Conn(context.Background()) if err != nil { - http.Error(w, "internal error", http.StatusInternalServerError) + http.Error(w, "internal error - error setting connection background", http.StatusInternalServerError) return } defer conn.Close() diff --git a/api/mark_posted.go b/api/mark_posted.go index 1981d51..549b0ef 100644 --- a/api/mark_posted.go +++ b/api/mark_posted.go @@ -29,7 +29,7 @@ func MarkPosted(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, user } if err := dbi.MarkPosted(conn, params.UID); err != nil { - jsonRPCErr(w, -32603, "Internal error", req.ID) + jsonRPCErr(w, -32603, "Internal error - error marking submissions as posted", req.ID) return } diff --git a/api/report_activity.go b/api/report_activity.go index e55e3a5..158885d 100644 --- a/api/report_activity.go +++ b/api/report_activity.go @@ -3,6 +3,7 @@ package api import ( "database/sql" "encoding/json" + "fmt" "net/http" "faculty_media_report/dbi" @@ -27,7 +28,8 @@ func ReportActivity(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, u reports.Activities[i].Status = "reported" } if err := dbi.CreateActivity(conn, &reports.Activities[i]); err != nil { - jsonRPCErr(w, -32603, "Internal error", req.ID) + err_msg := fmt.Sprintf("error saving Activity entry %s\n", err) + jsonRPCErr(w, -32603, err_msg, req.ID) return } } @@ -38,7 +40,8 @@ func ReportActivity(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, u reports.Appearances[i].Status = "reported" } if err := dbi.CreateAppearance(conn, &reports.Appearances[i]); err != nil { - jsonRPCErr(w, -32603, "Internal error", req.ID) + err_msg := fmt.Sprintf("error saving Appearance entry %s\n", err) + jsonRPCErr(w, -32603, err_msg, req.ID) return } } @@ -49,7 +52,8 @@ func ReportActivity(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, u reports.Scholarship[i].Status = "reported" } if err := dbi.CreateScholarship(conn, &reports.Scholarship[i]); err != nil { - jsonRPCErr(w, -32603, "Internal error", req.ID) + err_msg := fmt.Sprintf("error saving Scholarship entry %s\n", err) + jsonRPCErr(w, -32603, err_msg, req.ID) return } } diff --git a/main.go b/main.go index edaa7df..e2e5c59 100644 --- a/main.go +++ b/main.go @@ -3,20 +3,170 @@ 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() { + writeHTML(w, pages.LoginPage("Username or Password is incorrect.")) + } + + 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 { + + err_message := fmt.Sprintf("VALIDATION ERROR: %s\n", e) + w.Write([]byte(err_message)) + return + + } else if username == "" { + + w.Write([]byte("CAS ERROR: user not found")) + return + + } else { + + 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, e := dbi.GetUser(conn, username) + if e != nil { + login_error_message := fmt.Sprintf("User error for user %s: %s", e, username) + w.Write([]byte(login_error_message)) + return + } + + token, err := dbi.GenJWT(user) + if err != nil { + fail() + return + } + + if user.Status == "admin" { + + 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 + + } else if user.Status == "faculty" { + + writeHTML(w, pages.MainFormPage(token)) + return + + } else { + + w.Write([]byte("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("")) } @@ -89,8 +239,17 @@ func handleLoginPost(w http.ResponseWriter, r *http.Request) { writeHTML(w, pages.MainFormPage(token)) } +func getPortal(w http.ResponseWriter, r *http.Request) { + _, e := w.Write([]byte(pages.LandingPage)) + if e != nil { + writeHTML(w, pages.LoginPage("Username or Password is incorrect.")) + } +} + func main() { mux := http.NewServeMux() + mux.HandleFunc("GET /faculty/activity/cas", handleCas) + 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) diff --git a/pages/landing_page.go b/pages/landing_page.go new file mode 100644 index 0000000..07c5c48 --- /dev/null +++ b/pages/landing_page.go @@ -0,0 +1,155 @@ +package pages + +var LandingPage = ` + + + + + + + Landing page + + + + + + + + + + +
+ + + + Skip to main content + + + +
+ +
+
+ + + +
+ + + +
+
+
+
+ Maurer Applications +

Faculty Activity Portal

+
+

The Maurer School of Law Faculty Activity Portal is the central reporting center for faculty activity. Faculty can use this portal to keep the school up to date on their recent publications, talks, media appearances and other public academic contributions. Please direct any questions to the Associate Dean for Research and Faculty Affairs.

+
+ +
+
+ + +
+
+
+
+ + + + + + + + + +`