Compare commits
20
Commits
848a747fba
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac872d5127 | ||
|
|
ec901d97c2 | ||
|
|
a0c57bd582 | ||
|
|
793904e942 | ||
|
|
fd12ba02fd | ||
|
|
e6cebed4ba | ||
|
|
08ba0130c1 | ||
|
|
4a7b3c91f0 | ||
|
|
f76d715cb9 | ||
|
|
af2d823705 | ||
|
|
16a38c972c | ||
|
|
4644a7d3f4 | ||
|
|
c305b53f11 | ||
|
|
349ab612d6 | ||
|
|
c37a433984 | ||
|
|
8a8ebcdeec | ||
|
|
ab78dd711f | ||
|
|
1fddaf3f67 | ||
|
|
c3bee53ade | ||
|
|
9b0dea64fc |
@@ -1,2 +1,3 @@
|
||||
config.json
|
||||
.DS_Store
|
||||
token.json
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
A calandar viewer for stalwart calandars
|
||||
|
||||
**NOTE** while this app uses standard caldav reading it makes assumptions about paths and treats them as if they all come from stalwart
|
||||
|
||||
**TODO**
|
||||
1. Switch over to a golang caldav library, while I wanted to roll my own CalDAV I am too lazy to read up on the spec so imma just get someone else to do it
|
||||
2. Fix password login
|
||||
3. Actually write a UI that looks somewhat decent
|
||||
|
||||
+108
@@ -0,0 +1,108 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"astraltech.xyz/calendar/v2/caldav"
|
||||
"astraltech.xyz/calendar/v2/webserver"
|
||||
ics "github.com/arran4/golang-ical"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
var TestTokenStore TokenStore
|
||||
|
||||
func SetAuth() {
|
||||
TestTokenStore = TokenStore{filePath: "token.json"}
|
||||
}
|
||||
|
||||
func generateState() (string, error) {
|
||||
b := make([]byte, 32)
|
||||
if _, err := rand.Read(b); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b), nil
|
||||
}
|
||||
|
||||
func HandlePasswordAuth(authData webserver.PasswordAuthData) bool {
|
||||
calDavData := caldav.GetCalDAVData(authData.Username, authData.Password)
|
||||
|
||||
fmt.Printf("Users display name: %s\n", calDavData.DisplayName)
|
||||
fmt.Printf("Calandar Count: %d\n", len(calDavData.Calandars))
|
||||
for i := range len(calDavData.Calandars) {
|
||||
calDavData.Calandars[i] = caldav.GetCalendarData(calDavData.Calandars[i], authData.Username, authData.Password)
|
||||
|
||||
fmt.Printf("Cal %d is named %s\n", i, calDavData.Calandars[i].DisplayName)
|
||||
fmt.Printf("\tLink to cal: %s\n", calDavData.Calandars[i].Link)
|
||||
fmt.Printf("\tEvent count: %d\n", len(calDavData.Calandars[i].Events))
|
||||
for j := range len(calDavData.Calandars[i].Events) {
|
||||
cal, err := ics.ParseCalendar(strings.NewReader(calDavData.Calandars[i].Events[j].CalData))
|
||||
if err != nil {
|
||||
fmt.Print(err.Error())
|
||||
}
|
||||
events := cal.Events()
|
||||
for k := range len(events) {
|
||||
name := events[k].GetProperty(ics.ComponentProperty(ics.PropertySummary)).Value
|
||||
start, _ := events[k].GetStartAt()
|
||||
end, _ := events[k].GetEndAt()
|
||||
time := end.Sub(start)
|
||||
fmt.Printf("\t\tEvent name: %s\n", name)
|
||||
fmt.Printf("\t\t\tEvent start date: %s\n", end.UTC().String())
|
||||
fmt.Printf("\t\t\tEvent length date: %s\n", time.String())
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func HandleOAuth(authData webserver.OAuthAuthData) bool {
|
||||
state, err := generateState()
|
||||
if err != nil {
|
||||
http.Error(*authData.ResponseWriter, "Failed to generate state", http.StatusInternalServerError)
|
||||
return false
|
||||
}
|
||||
verifier := oauth2.GenerateVerifier()
|
||||
|
||||
http.SetCookie(*authData.ResponseWriter, &http.Cookie{
|
||||
Name: "oauth_state",
|
||||
Value: state,
|
||||
Path: "/",
|
||||
MaxAge: 300,
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
http.SetCookie(*authData.ResponseWriter, &http.Cookie{
|
||||
Name: "pkce_verifier",
|
||||
Value: verifier,
|
||||
Path: "/",
|
||||
HttpOnly: true,
|
||||
SameSite: http.SameSiteLaxMode,
|
||||
})
|
||||
|
||||
url := DefaultProvider.Config.AuthCodeURL(
|
||||
state,
|
||||
oauth2.S256ChallengeOption(verifier),
|
||||
)
|
||||
http.Redirect(*authData.ResponseWriter, authData.Request, url, http.StatusFound)
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func HandleAuthRequest(authType webserver.AuthStyle, authData webserver.AuthData) bool {
|
||||
if authType == webserver.AuthStylePassword {
|
||||
passwordAuthData, _ := authData.(webserver.PasswordAuthData)
|
||||
return HandlePasswordAuth(passwordAuthData)
|
||||
}
|
||||
|
||||
if authType == webserver.AuthStyleOAuth {
|
||||
passwordAuthData, _ := authData.(webserver.OAuthAuthData)
|
||||
return HandleOAuth(passwordAuthData)
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// This is all the user info that this script needs
|
||||
type UserInfo struct {
|
||||
Email string `json:"email"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type OAuthProvider struct {
|
||||
Provider *oidc.Provider
|
||||
Config oauth2.Config
|
||||
}
|
||||
|
||||
var Providers []OAuthProvider
|
||||
var DefaultProvider *OAuthProvider // Assumed to be the last created provider
|
||||
|
||||
func CreateOAuthProvider(issuer string, clientID string) error {
|
||||
ctx := context.Background()
|
||||
provider, err := oidc.NewProvider(ctx, issuer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
OAuthConfig := oauth2.Config{
|
||||
ClientID: clientID,
|
||||
RedirectURL: "http://localhost:8080/callback",
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: []string{
|
||||
oidc.ScopeOpenID,
|
||||
"profile",
|
||||
"email",
|
||||
},
|
||||
}
|
||||
|
||||
newProvider := OAuthProvider{
|
||||
Provider: provider,
|
||||
Config: OAuthConfig,
|
||||
}
|
||||
Providers = append(Providers, newProvider)
|
||||
DefaultProvider = &newProvider
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// this is usually served at /callback
|
||||
func OAuthCallback(w http.ResponseWriter, r *http.Request) {
|
||||
state := r.FormValue("state")
|
||||
code := r.FormValue("code")
|
||||
|
||||
stateCookie, err := r.Cookie("oauth_state")
|
||||
if err != nil {
|
||||
http.Error(w, "Invalid state parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if state != stateCookie.Value {
|
||||
http.Error(w, "Invalid state parameter", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "oauth_state",
|
||||
Value: "",
|
||||
Path: "/",
|
||||
MaxAge: -1,
|
||||
})
|
||||
|
||||
verifierCookie, err := r.Cookie("pkce_verifier")
|
||||
if err != nil {
|
||||
http.Error(w, "Missing PKCE verifier", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
verifier := verifierCookie.Value
|
||||
|
||||
token, err := DefaultProvider.Config.Exchange(context.Background(), code, oauth2.VerifierOption(verifier))
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to exchange token: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
if err := TestTokenStore.Save(token); err != nil {
|
||||
log.Printf("Failed to save token: %v", err)
|
||||
}
|
||||
|
||||
http.Redirect(w, r, "/calandar", http.StatusTemporaryRedirect)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
type TokenStore struct {
|
||||
filePath string
|
||||
}
|
||||
|
||||
func (ts *TokenStore) Save(token *oauth2.Token) error {
|
||||
data, err := json.Marshal(token)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(ts.filePath, data, 0600)
|
||||
}
|
||||
|
||||
func (ts *TokenStore) Load() (*oauth2.Token, error) {
|
||||
data, err := os.ReadFile(ts.filePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var token oauth2.Token
|
||||
if err := json.Unmarshal(data, &token); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &token, nil
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package caldav
|
||||
|
||||
type MultiStatus struct {
|
||||
Responses []Response `xml:"response"`
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package caldav
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
@@ -20,10 +20,12 @@ const neededXML string = `
|
||||
</d:propfind>
|
||||
`
|
||||
|
||||
var client *http.Client
|
||||
var BuildInClient *http.Client
|
||||
var serverURL string
|
||||
|
||||
func init_dav_client() {
|
||||
client = &http.Client{}
|
||||
func InitDavClient(url string) {
|
||||
BuildInClient = &http.Client{}
|
||||
serverURL = url
|
||||
}
|
||||
|
||||
type Event struct {
|
||||
@@ -41,11 +43,10 @@ type SimpleCalDavData struct {
|
||||
Calandars []Calandar
|
||||
}
|
||||
|
||||
// this makes stalwart assumptions
|
||||
func get_caldav_data(username string, password string) SimpleCalDavData {
|
||||
func GetCalDAVData(username string, password string) SimpleCalDavData {
|
||||
req, _ := http.NewRequest(
|
||||
"PROPFIND",
|
||||
serverConfig.URL+"/dav/cal/"+convert_username_into_url(username),
|
||||
serverURL+"/dav/cal/"+convert_username_into_url(username),
|
||||
strings.NewReader(neededXML),
|
||||
)
|
||||
|
||||
@@ -53,7 +54,7 @@ func get_caldav_data(username string, password string) SimpleCalDavData {
|
||||
req.Header.Set("Depth", "1")
|
||||
req.Header.Set("Content-Type", "application/xml")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
resp, err := BuildInClient.Do(req)
|
||||
if err != nil {
|
||||
fmt.Print(err.Error())
|
||||
}
|
||||
@@ -61,13 +62,13 @@ func get_caldav_data(username string, password string) SimpleCalDavData {
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fmt.Print(err.Error())
|
||||
}
|
||||
|
||||
var davResp MultiStatus
|
||||
err = xml.Unmarshal(body, &davResp)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
fmt.Print(err)
|
||||
}
|
||||
|
||||
calandars := []Calandar{}
|
||||
@@ -78,7 +79,7 @@ func get_caldav_data(username string, password string) SimpleCalDavData {
|
||||
|
||||
var new_cal Calandar = Calandar{
|
||||
DisplayName: davResp.Responses[i].PropStat[0].Prop.DisplayName,
|
||||
Link: serverConfig.URL + davResp.Responses[i].Href,
|
||||
Link: serverURL + davResp.Responses[i].Href,
|
||||
}
|
||||
|
||||
calandars = append(calandars, new_cal)
|
||||
@@ -107,7 +108,7 @@ const XML_cal string = `
|
||||
</C:calendar-query>
|
||||
`
|
||||
|
||||
func get_calandar_data(cal Calandar, username string, password string) Calandar {
|
||||
func GetCalendarData(cal Calandar, username string, password string) Calandar {
|
||||
req, _ := http.NewRequest(
|
||||
"REPORT",
|
||||
cal.Link,
|
||||
@@ -118,7 +119,7 @@ func get_calandar_data(cal Calandar, username string, password string) Calandar
|
||||
req.Header.Set("Depth", "1")
|
||||
req.Header.Set("Content-Type", "application/xml")
|
||||
|
||||
resp, err := client.Do(req)
|
||||
resp, err := BuildInClient.Do(req)
|
||||
if err != nil {
|
||||
fmt.Print(err.Error())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package caldav
|
||||
|
||||
import "strings"
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"url:": "https://mx.example.com",
|
||||
"username": "admin@example.com",
|
||||
"password": "password"
|
||||
"caldav_url:": "https://mx.example.com",
|
||||
"oauth_enabled": true,
|
||||
"oauth_issuer": "https://sso.example.com/application/o/stalwart/",
|
||||
"oauth_clientid": "stalwart-webui"
|
||||
}
|
||||
|
||||
@@ -2,4 +2,9 @@ module astraltech.xyz/calendar/v2
|
||||
|
||||
go 1.26.1
|
||||
|
||||
require github.com/arran4/golang-ical v0.3.5 // indirect
|
||||
require (
|
||||
github.com/arran4/golang-ical v0.3.5
|
||||
github.com/coreos/go-oidc/v3 v3.20.0
|
||||
github.com/go-jose/go-jose/v4 v4.1.4
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
)
|
||||
|
||||
@@ -1,2 +1,8 @@
|
||||
github.com/arran4/golang-ical v0.3.5 h1:bbz6ld4dC+MmCKiFfOd6SkmIGnhNMBACZ485ULh7p9A=
|
||||
github.com/arran4/golang-ical v0.3.5/go.mod h1:OnguFgjN0Hmx8jzpmWcC+AkHio94ujmLHKoaef7xQh8=
|
||||
github.com/coreos/go-oidc/v3 v3.20.0 h1:EtE0WIBHk03N+DqGkY4+UONzzZHk7amKt6IyNd7OsZE=
|
||||
github.com/coreos/go-oidc/v3 v3.20.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
|
||||
github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"text/template"
|
||||
|
||||
"astraltech.xyz/calendar/v2/auth"
|
||||
)
|
||||
|
||||
type CalendarData struct {
|
||||
DisplayName string
|
||||
}
|
||||
|
||||
func ParseOAuthLogin(w http.ResponseWriter, r *http.Request) CalendarData {
|
||||
token, err := auth.TestTokenStore.Load()
|
||||
if err != nil {
|
||||
http.Error(w, "Not authenticated", http.StatusUnauthorized)
|
||||
return CalendarData{}
|
||||
}
|
||||
|
||||
client := auth.DefaultProvider.Config.Client(context.Background(), token)
|
||||
|
||||
resp, err := client.Get(auth.DefaultProvider.Provider.UserInfoEndpoint())
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to fetch user: "+err.Error(), http.StatusInternalServerError)
|
||||
return CalendarData{}
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var usersInfo auth.UserInfo
|
||||
if err := json.NewDecoder(resp.Body).Decode(&usersInfo); err != nil {
|
||||
http.Error(w, "Failed to decode response", http.StatusInternalServerError)
|
||||
return CalendarData{}
|
||||
}
|
||||
return CalendarData{
|
||||
DisplayName: usersInfo.Name,
|
||||
}
|
||||
}
|
||||
|
||||
func HandleCalandarEndpoint(w http.ResponseWriter, r *http.Request) {
|
||||
var OauthLogin = true
|
||||
var data CalendarData
|
||||
if OauthLogin {
|
||||
data = ParseOAuthLogin(w, r)
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
tmpl := template.Must(template.ParseFiles("main/pages/calendar.html"))
|
||||
tmpl.Execute(w, data)
|
||||
}
|
||||
+4
-3
@@ -7,9 +7,10 @@ import (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
URL string `json:"url"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
URL string `json:"caldav_url"`
|
||||
OAuthEnabled bool `json:"oauth_enabled"`
|
||||
OAuthIssuer string `json:"oauth_issuer"`
|
||||
OAuthClientID string `json:"oauth_clientid"`
|
||||
}
|
||||
|
||||
var serverConfig Config
|
||||
|
||||
+13
-39
@@ -1,55 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"astraltech.xyz/calendar/v2/auth"
|
||||
"astraltech.xyz/calendar/v2/caldav"
|
||||
"astraltech.xyz/calendar/v2/webserver"
|
||||
)
|
||||
|
||||
func HandleAuthRequest(username string, password string) bool {
|
||||
fmt.Printf("Handling Auth request for %s", username)
|
||||
return false
|
||||
}
|
||||
|
||||
func main() {
|
||||
auth.SetAuth()
|
||||
read_config()
|
||||
init_dav_client()
|
||||
|
||||
if serverConfig.OAuthEnabled {
|
||||
auth.CreateOAuthProvider(serverConfig.OAuthIssuer, serverConfig.OAuthClientID)
|
||||
}
|
||||
|
||||
caldav.InitDavClient(serverConfig.URL)
|
||||
|
||||
webserver.ServeLoginPage("/login", webserver.CustomizableLoginData{
|
||||
ServiceName: "Astral Calendar",
|
||||
AuthRequestFunction: HandleAuthRequest,
|
||||
AuthRequestFunction: auth.HandleAuthRequest,
|
||||
})
|
||||
webserver.ServeWebpage("/callback", auth.OAuthCallback)
|
||||
webserver.ServeWebpage("/calandar", HandleCalandarEndpoint)
|
||||
|
||||
webserver.EnableLogoRoute()
|
||||
webserver.EnableStaticRoute()
|
||||
webserver.ServeWebserver()
|
||||
|
||||
// calDavData := get_caldav_data(serverConfig.Username, serverConfig.Password)
|
||||
// fmt.Println()
|
||||
|
||||
// fmt.Printf("Users display name: %s\n", calDavData.DisplayName)
|
||||
// fmt.Printf("Calandar Count: %d\n", len(calDavData.Calandars))
|
||||
// for i := range len(calDavData.Calandars) {
|
||||
// calDavData.Calandars[i] = get_calandar_data(calDavData.Calandars[i], serverConfig.Username, serverConfig.Password)
|
||||
|
||||
// fmt.Printf("Cal %d is named %s\n", i, calDavData.Calandars[i].DisplayName)
|
||||
// fmt.Printf("\tLink to cal: %s\n", calDavData.Calandars[i].Link)
|
||||
// fmt.Printf("\tEvent count: %d\n", len(calDavData.Calandars[i].Events))
|
||||
// for j := range len(calDavData.Calandars[i].Events) {
|
||||
// cal, err := ics.ParseCalendar(strings.NewReader(calDavData.Calandars[i].Events[j].CalData))
|
||||
// if err != nil {
|
||||
// fmt.Print(err.Error())
|
||||
// }
|
||||
// events := cal.Events()
|
||||
// for k := range len(events) {
|
||||
// name := events[k].GetProperty(ics.ComponentProperty(ics.PropertySummary)).Value
|
||||
// start, _ := events[k].GetStartAt()
|
||||
// end, _ := events[k].GetEndAt()
|
||||
// time := end.Sub(start)
|
||||
// fmt.Printf("\t\tEvent name: %s\n", name)
|
||||
// fmt.Printf("\t\t\tEvent start date: %s\n", end.UTC().String())
|
||||
// fmt.Printf("\t\t\tEvent length date: %s\n", time.String())
|
||||
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Astral Calendar</title>
|
||||
</head>
|
||||
<body>
|
||||
Welcome to your calendar {{.DisplayName}}
|
||||
</body>
|
||||
</html>
|
||||
+35
-2
@@ -10,10 +10,28 @@ type LoginPageData struct {
|
||||
IsHiddenClassList string
|
||||
LoginData CustomizableLoginData
|
||||
}
|
||||
type AuthStyle int
|
||||
|
||||
const (
|
||||
AuthStylePassword AuthStyle = 0
|
||||
AuthStyleOAuth AuthStyle = 1
|
||||
)
|
||||
|
||||
type AuthData interface{}
|
||||
|
||||
type PasswordAuthData struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
type OAuthAuthData struct {
|
||||
ResponseWriter *http.ResponseWriter
|
||||
Request *http.Request
|
||||
}
|
||||
|
||||
type CustomizableLoginData struct {
|
||||
ServiceName string
|
||||
AuthRequestFunction func(string, string) bool
|
||||
AuthRequestFunction func(AuthStyle, AuthData) bool
|
||||
}
|
||||
|
||||
var LoginPageDataCustomizations CustomizableLoginData
|
||||
@@ -27,15 +45,30 @@ func loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if r.Method == http.MethodPost {
|
||||
action := r.FormValue("action")
|
||||
|
||||
if action == "password" {
|
||||
username := r.FormValue("username")
|
||||
if strings.Contains(username, "/") {
|
||||
tmpl.Execute(w, LoginPageData{IsHiddenClassList: "", LoginData: LoginPageDataCustomizations})
|
||||
}
|
||||
password := r.FormValue("password")
|
||||
|
||||
auth_success := LoginPageDataCustomizations.AuthRequestFunction(username, password)
|
||||
auth_success := LoginPageDataCustomizations.AuthRequestFunction(AuthStylePassword, PasswordAuthData{
|
||||
Username: username,
|
||||
Password: password,
|
||||
})
|
||||
if auth_success == false {
|
||||
tmpl.Execute(w, LoginPageData{IsHiddenClassList: "", LoginData: LoginPageDataCustomizations})
|
||||
}
|
||||
} else {
|
||||
auth_success := LoginPageDataCustomizations.AuthRequestFunction(AuthStyleOAuth, OAuthAuthData{
|
||||
ResponseWriter: &w,
|
||||
Request: r,
|
||||
})
|
||||
if auth_success == false {
|
||||
tmpl.Execute(w, LoginPageData{IsHiddenClassList: "", LoginData: LoginPageDataCustomizations})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,17 +23,20 @@
|
||||
|
||||
<div>
|
||||
<label class="input_label">Username</label><br />
|
||||
<input type="text" name="username" placeholder="" required />
|
||||
<input type="text" name="username" placeholder="" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="input_label">Password</label><br />
|
||||
<div class="password_box">
|
||||
<input type="password" name="password" placeholder="" required />
|
||||
<input type="password" name="password" placeholder="" />
|
||||
<button type="button" class="show_password_toggle closed"></button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit">Login</button>
|
||||
<div id="login_buttons">
|
||||
<button type="submit" name="action" value="password">Login with password</button>
|
||||
<button type="submit" id="login_with_oauth" name="action" value="oauth"><img src="/favicon.ico"/></button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script src="/static/error/error.js" type="text/javascript"></script>
|
||||
|
||||
@@ -33,3 +33,23 @@
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
#login_buttons {
|
||||
display: grid;
|
||||
grid-gap: 5px;
|
||||
grid-template-columns: 1fr 43px;
|
||||
}
|
||||
|
||||
#login_with_oauth {
|
||||
padding: 0 !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#login_with_oauth img {
|
||||
position: absolute;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user