reprogl

Форк
0
/
auth.go 
157 строк · 4.5 Кб
1
package handlers
2

3
import (
4
	"fmt"
5
	"net/http"
6
	"time"
7

8
	"github.com/xelbot/yetacache"
9
	"xelbot.com/reprogl/container"
10
	"xelbot.com/reprogl/models"
11
	"xelbot.com/reprogl/models/repositories"
12
	"xelbot.com/reprogl/services/auth"
13
	"xelbot.com/reprogl/session"
14
	"xelbot.com/reprogl/views"
15
)
16

17
func LoginAction(app *container.Application) http.HandlerFunc {
18
	return func(w http.ResponseWriter, r *http.Request) {
19
		var csrfToken string
20
		var found bool
21

22
		cache := app.GetStringCache()
23

24
		if cookie, errNoCookie := r.Cookie(session.CsrfCookie); errNoCookie != nil {
25
			csrfToken = generateCsrfPair(w, cache)
26
		} else {
27
			csrfTokenKey := cookie.Value
28
			if csrfToken, found = cache.Get(csrfTokenKey); !found {
29
				csrfToken = generateCsrfPair(w, cache)
30
			}
31
		}
32

33
		saveLoginReferer(w, r)
34
		errorMessage, hasError := session.Pop[string](r.Context(), session.FlashErrorKey)
35

36
		templateData := views.NewLoginPageData(csrfToken, errorMessage, hasError)
37
		err := views.WriteTemplate(w, "login.gohtml", templateData)
38
		if err != nil {
39
			app.ServerError(w, err)
40

41
			return
42
		}
43
	}
44
}
45

46
func LoginCheck(app *container.Application) http.HandlerFunc {
47
	return func(w http.ResponseWriter, r *http.Request) {
48
		var csrfToken string
49
		var found bool
50

51
		if session.HasIdentity(r.Context()) {
52
			session.ClearIdentity(r.Context())
53
		}
54

55
		cache := app.GetStringCache()
56
		if cookie, errNoCookie := r.Cookie(session.CsrfCookie); errNoCookie == nil {
57
			csrfTokenKey := cookie.Value
58
			if csrfToken, found = cache.Get(csrfTokenKey); !found {
59
				deleteCsrfCookie(w)
60
				session.Put(r.Context(), session.FlashErrorKey, "Непонятная ошибка")
61
				app.InfoLog.Println("[AUTH] not found CSRF-token in cache")
62
				http.Redirect(w, r, container.GenerateURL("login"), http.StatusSeeOther)
63
				return
64
			}
65
		}
66

67
		err := r.ParseForm()
68
		if err != nil {
69
			deleteCsrfCookie(w)
70
			app.ClientError(w, http.StatusBadRequest)
71
			return
72
		}
73

74
		formCsrfToken := r.PostForm.Get("_csrf_token")
75
		if formCsrfToken != csrfToken {
76
			deleteCsrfCookie(w)
77
			session.Put(r.Context(), session.FlashErrorKey, "Непонятная ошибка")
78
			app.InfoLog.Println("[AUTH] wrong CSRF-token")
79
			http.Redirect(w, r, container.GenerateURL("login"), http.StatusSeeOther)
80
			return
81
		}
82

83
		user, err := auth.HandleLoginPassword(app, r.PostForm.Get("username"), r.PostForm.Get("password"))
84
		if err != nil {
85
			deleteCsrfCookie(w)
86
			if authError, found := err.(auth.Error); found {
87
				session.Put(r.Context(), session.FlashErrorKey, err.Error())
88
				app.InfoLog.Println("[AUTH] " + authError.InfoLogMessage())
89
				http.Redirect(w, r, container.GenerateURL("login"), http.StatusSeeOther)
90
			} else {
91
				app.ServerError(w, err)
92
			}
93

94
			return
95
		}
96

97
		session.Put(r.Context(), session.FlashSuccessKey, fmt.Sprintf("Привет, %s :)", user.Username))
98
		app.InfoLog.Printf("[AUTH] success for \"%s\"\n", user.Username)
99
		authSuccess(user, app, container.RealRemoteAddress(r), r.Context())
100

101
		var redirectUrl string
102
		if redirectUrl, found = popLoginReferer(w, r); !found {
103
			redirectUrl = "/"
104
		}
105

106
		deleteCsrfCookie(w)
107
		http.Redirect(w, r, redirectUrl, http.StatusFound)
108
	}
109
}
110

111
func LogoutAction(w http.ResponseWriter, r *http.Request) {
112
	session.Destroy(r.Context())
113
	http.Redirect(w, r, "/", http.StatusFound)
114
}
115

116
func AuthNavigation(app *container.Application) http.HandlerFunc {
117
	return func(w http.ResponseWriter, r *http.Request) {
118
		cacheControl(w, container.DefaultEsiTTL)
119
		templateData := views.NewAuthNavigationData()
120
		err := views.WriteTemplateWithContext(r.Context(), w, "auth-navigation.gohtml", templateData)
121
		if err != nil {
122
			app.ServerError(w, err)
123
		}
124
	}
125
}
126

127
func MenuAuth(app *container.Application) http.HandlerFunc {
128
	return func(w http.ResponseWriter, r *http.Request) {
129
		var user *models.User
130
		if identity, ok := session.GetIdentity(r.Context()); ok {
131
			repo := repositories.UserRepository{DB: app.DB}
132
			user, _ = repo.Find(identity.ID)
133
		}
134

135
		templateData := views.NewMenuAuthData(user)
136
		cacheControl(w, container.DefaultEsiTTL)
137

138
		err := views.WriteTemplateWithContext(r.Context(), w, "menu-auth.gohtml", templateData)
139
		if err != nil {
140
			app.ServerError(w, err)
141
		}
142
	}
143
}
144

145
func generateCsrfPair(w http.ResponseWriter, cache *yetacache.Cache[string, string]) string {
146
	csrfToken := generateRandomToken()
147
	csrfTokenKey := generateRandomToken()
148

149
	cache.Set(csrfTokenKey, csrfToken, 30*time.Minute)
150
	session.WriteSessionCookie(w, session.CsrfCookie, csrfTokenKey, "/login")
151

152
	return csrfToken
153
}
154

155
func deleteCsrfCookie(w http.ResponseWriter) {
156
	session.DeleteCookie(w, session.CsrfCookie, "/login")
157
}
158

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.