portal page

master
Joshua Herring 3 weeks ago
parent 06096009f0
commit de6b629994

@ -54,14 +54,14 @@ func HandleAPI(w http.ResponseWriter, r *http.Request) {
db, err := dbi.GetDbConn() db, err := dbi.GetDbConn()
if err != nil { if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError) http.Error(w, "internal error - error getting db connection", http.StatusInternalServerError)
return return
} }
defer db.Close() defer db.Close()
conn, err := db.Conn(context.Background()) conn, err := db.Conn(context.Background())
if err != nil { if err != nil {
http.Error(w, "internal error", http.StatusInternalServerError) http.Error(w, "internal error - error setting connection background", http.StatusInternalServerError)
return return
} }
defer conn.Close() defer conn.Close()

@ -29,7 +29,7 @@ func MarkPosted(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, user
} }
if err := dbi.MarkPosted(conn, params.UID); err != nil { 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 return
} }

@ -3,6 +3,7 @@ package api
import ( import (
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"faculty_media_report/dbi" "faculty_media_report/dbi"
@ -27,7 +28,8 @@ func ReportActivity(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, u
reports.Activities[i].Status = "reported" reports.Activities[i].Status = "reported"
} }
if err := dbi.CreateActivity(conn, &reports.Activities[i]); err != nil { 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 return
} }
} }
@ -38,7 +40,8 @@ func ReportActivity(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, u
reports.Appearances[i].Status = "reported" reports.Appearances[i].Status = "reported"
} }
if err := dbi.CreateAppearance(conn, &reports.Appearances[i]); err != nil { 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 return
} }
} }
@ -49,7 +52,8 @@ func ReportActivity(w http.ResponseWriter, conn *sql.Conn, req jsonRPCRequest, u
reports.Scholarship[i].Status = "reported" reports.Scholarship[i].Status = "reported"
} }
if err := dbi.CreateScholarship(conn, &reports.Scholarship[i]); err != nil { 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 return
} }
} }

@ -3,20 +3,170 @@ package main
import ( import (
"context" "context"
"crypto/md5" "crypto/md5"
"encoding/xml"
"fmt" "fmt"
"io"
"net/http" "net/http"
"net/http/cgi" "net/http/cgi"
"strings"
"faculty_media_report/api" "faculty_media_report/api"
"faculty_media_report/dbi" "faculty_media_report/dbi"
"faculty_media_report/pages" "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 = `<?xml version="1.0" encoding="UTF-8"?>`
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) { func writeHTML(w http.ResponseWriter, html string) {
w.Header().Set("Content-Type", "text/html; charset=utf-8") w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprint(w, html) fmt.Fprint(w, html)
} }
func handleCas(w http.ResponseWriter, r *http.Request) {
}
func handleLoginGet(w http.ResponseWriter, r *http.Request) { func handleLoginGet(w http.ResponseWriter, r *http.Request) {
writeHTML(w, pages.LoginPage("")) writeHTML(w, pages.LoginPage(""))
} }
@ -89,8 +239,17 @@ func handleLoginPost(w http.ResponseWriter, r *http.Request) {
writeHTML(w, pages.MainFormPage(token)) 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() { func main() {
mux := http.NewServeMux() 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("GET /faculty/activity/login", handleLoginGet)
mux.HandleFunc("POST /faculty/activity/login", handleLoginPost) mux.HandleFunc("POST /faculty/activity/login", handleLoginPost)
mux.HandleFunc("POST /faculty/activity/api", api.HandleAPI) mux.HandleFunc("POST /faculty/activity/api", api.HandleAPI)

@ -0,0 +1,155 @@
package pages
var LandingPage = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex">
<title>Landing page</title>
<link rel="stylesheet" href="https://unpkg.com/rivet-core@2.9.1/css/rivet.min.css">
<link rel="stylesheet" href="https://unpkg.com/rivet-stickers@0.3.1/dist/rivet-sticker-element.css">
<script type="module" src="https://unpkg.com/rivet-stickers@0.3.1/dist/rivet-stickers.js"></script>
</head>
<body class="rvt-layout">
<!-- **************************************************************************
LANDING PAGE
-> rivet.iu.edu/layouts/index-page/
*************************************************************************** -->
<!-- **************************************************************************
Header
-> rivet.iu.edu/components/header/
*************************************************************************** -->
<header class="rvt-header-wrapper">
<!-- **********************************************************************
"Skip to main content" link for keyboard users
*********************************************************************** -->
<a class="rvt-header-wrapper__skip-link" href="#main-content">Skip to main content</a>
<!-- **********************************************************************
Global header area
*********************************************************************** -->
<div class="rvt-header-global">
<div class="rvt-container-lg">
<div class="rvt-header-global__inner">
<div class="rvt-header-global__logo-slot">
<a class="rvt-lockup" href="#0">
<!-- **************************************************
Trident logo
*************************************************** -->
<div class="rvt-lockup__tab">
<svg xmlns="http://www.w3.org/2000/svg" class="rvt-lockup__trident" viewBox="0 0 28 34">
<path fill="currentColor" d="M-3.34344e-05 4.70897H8.83308V7.174H7.1897V21.1426H10.6134V2.72321H8.83308V0.121224H18.214V2.65476H16.2283V21.1426H19.7889V7.174H18.214V4.64047H27.0471V7.174H25.0614V23.6761L21.7746 26.8944H16.2967V30.455H18.214V33.8787H8.76463V30.592H10.6819V26.8259H5.20403L1.91726 23.6077V7.174H-3.34344e-05V4.70897Z"></path>
</svg>
</div>
<!-- **************************************************
Website or application title
*************************************************** -->
<div class="rvt-lockup__body">
<span class="rvt-lockup__title">Maurer School of Law</span>
<span class="rvt-lockup__subtitle">Indiana University</span>
</div>
</a>
</div>
<div class="rvt-header-global__controls">
<div data-rvt-disclosure="menu" data-rvt-close-click-outside>
<button aria-expanded="false" class="rvt-global-toggle rvt-global-toggle--menu rvt-hide-lg-up" data-rvt-disclosure-toggle="menu">
<span class="rvt-sr-only">Menu</span>
<svg class="rvt-global-toggle__open" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="M15 4H1V2h14v2Zm0 5H1V7h14v2ZM1 14h14v-2H1v2Z"/></svg>
<svg class="rvt-global-toggle__close" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 16 16"> <path d="m3.5 2.086 4.5 4.5 4.5-4.5L13.914 3.5 9.414 8l4.5 4.5-1.414 1.414-4.5-4.5-4.5 4.5L2.086 12.5l4.5-4.5-4.5-4.5L3.5 2.086Z"/></svg>
</button>
<!-- ******************************************************
Primary navigation
******************************************************* -->
</div>
</div>
</div>
</div>
</div>
</header>
<!-- **************************************************************************
Main content area
*************************************************************************** -->
<main id="main-content" class="rvt-layout__wrapper">
<!-- **********************************************************************
Hero
*********************************************************************** -->
<div class="rvt-hero">
<div class="rvt-container-lg">
<div class="rvt-hero__inner">
<div class="rvt-hero__body [ rvt-flow ]">
<span class="rvt-hero__eyebrow">Maurer Applications</span>
<h1 class="rvt-hero__title">Faculty Activity Portal</h1>
<div class="rvt-hero__teaser">
<p>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.</p>
</div>
<div class="rvt-hero__actions">
<a class="rvt-cta rvt-cta--button" href="/faculty/activity/cas">Log in with CAS</a>
</div>
</div>
<div class="rvt-hero__media">
<rvt-sticker name="megaphone"></rvt-sticker>
<!---<img src="https://rivet.iu.edu/img/placeholder/hero-2.webp" alt="Person wearing a mortarboard and graduation gown against a crimson background">--->
</div>
</div>
</div>
</div>
<!-- **************************************************************************
Footer: Copyright
-> rivet.iu.edu/components/footer/
**************************************************************************** -->
<footer class="rvt-footer-base">
<div class="rvt-container-lg">
<div class="rvt-footer-base__inner">
<div class="rvt-footer-base__logo">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
<polygon fill="currentColor" points="15.3 3.19 15.3 5 16.55 5 16.55 15.07 13.9 15.07 13.9 1.81 15.31 1.81 15.31 0 8.72 0 8.72 1.81 10.12 1.81 10.12 15.07 7.45 15.07 7.45 5 8.7 5 8.7 3.19 2.5 3.19 2.5 5 3.9 5 3.9 16.66 6.18 18.98 10.12 18.98 10.12 21.67 8.72 21.67 8.72 24 15.3 24 15.3 21.67 13.9 21.67 13.9 18.98 17.82 18.98 20.09 16.66 20.09 5 21.5 5 21.5 3.19 15.3 3.19" fill="#231f20" />
</svg>
</div>
<ul class="rvt-footer-base__list">
<li class="rvt-footer-base__item">
<a class="rvt-footer-base__link" href="https://faculty.law.indiana.edu">Faculty Portal</a>
</li>
<li class="rvt-footer-base__item">
<a class="rvt-footer-base__link" href="https://accessibility.iu.edu/assistance/">Accessibility</a>
</li>
<li class="rvt-footer-base__item">
<a class="rvt-footer-base__link" href="#0">Privacy Notice</a>
</li>
<li class="rvt-footer-base__item">
<a class="rvt-footer-base__link" href="#0">Copyright</a> © 2026 The Trustees of <a class="rvt-footer-base__link" href="https://www.iu.edu">Indiana University</a>
</li>
</ul>
</div>
</div>
</footer>
<script src="https://unpkg.com/rivet-core@2.9.1/js/rivet.min.js"></script>
<script>
Rivet.init();
</script>
</body>
</html>
`
Loading…
Cancel
Save