Goit

Simple and lightweight Git web server
git clone http://git.omkov.net/Goit
Log | Tree | Refs | README | Download

Goit/src/goit/auth.go (213 lines, 4.5 KiB) -rw-r--r-- file download

ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
0
// Copyright (C) 2023, Jakob Wakeling
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
1
// All rights reserved.
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
2
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
3
package goit
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
4
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
5
import (
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
6
	"crypto/rand"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
7
	"encoding/base64"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
8
	"fmt"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
9
	"log"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
10
	"net/http"
0893c1e Jakob Wakeling 2023-07-21 17:11:15
11
	"strconv"
0893c1e Jakob Wakeling 2023-07-21 17:11:15
12
	"strings"
410f65b Jakob Wakeling 2023-10-23 15:14:42
13
	"sync"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
14
	"time"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
15
d631c5e Jakob Wakeling 2023-07-20 23:13:39
16
	"github.com/Jamozed/Goit/src/util"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
17
	"golang.org/x/crypto/argon2"
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
18
)
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
19
0893c1e Jakob Wakeling 2023-07-21 17:11:15
20
type Session struct {
52b258d Jakob Wakeling 2023-07-21 19:22:31
21
	Token, Ip    string
52b258d Jakob Wakeling 2023-07-21 19:22:31
22
	Seen, Expiry time.Time
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
23
}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
24
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
25
var Sessions = map[int64][]Session{}
410f65b Jakob Wakeling 2023-10-23 15:14:42
26
var SessionsMutex = sync.RWMutex{}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
27
410f65b Jakob Wakeling 2023-10-23 15:14:42
28
/* Generate a new user session. */
0893c1e Jakob Wakeling 2023-07-21 17:11:15
29
func NewSession(uid int64, ip string, expiry time.Time) (Session, error) {
410f65b Jakob Wakeling 2023-10-23 15:14:42
30
	var b = make([]byte, 24)
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
31
	if _, err := rand.Read(b); err != nil {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
32
		return Session{}, err
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
33
	}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
34
410f65b Jakob Wakeling 2023-10-23 15:14:42
35
	var t = base64.StdEncoding.EncodeToString(b)
410f65b Jakob Wakeling 2023-10-23 15:14:42
36
	var s = Session{Token: t, Ip: util.If(Conf.IpSessions, ip, ""), Seen: time.Now(), Expiry: expiry}
410f65b Jakob Wakeling 2023-10-23 15:14:42
37
410f65b Jakob Wakeling 2023-10-23 15:14:42
38
	SessionsMutex.Lock()
570144e Jakob Wakeling 2023-12-15 23:28:06
39
	util.Debugln("[goit.NewSession] SessionsMutex lock")
5166d87 Jakob Wakeling 2023-10-23 16:00:35
40
0893c1e Jakob Wakeling 2023-07-21 17:11:15
41
	if Sessions[uid] == nil {
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
42
		Sessions[uid] = []Session{}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
43
	}
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
44
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
45
	Sessions[uid] = append(Sessions[uid], s)
5166d87 Jakob Wakeling 2023-10-23 16:00:35
46
410f65b Jakob Wakeling 2023-10-23 15:14:42
47
	SessionsMutex.Unlock()
570144e Jakob Wakeling 2023-12-15 23:28:06
48
	util.Debugln("[goit.EndSession] SessionsMutex unlock")
410f65b Jakob Wakeling 2023-10-23 15:14:42
49
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
50
	return s, nil
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
51
}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
52
410f65b Jakob Wakeling 2023-10-23 15:14:42
53
/* End a user session. */
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
54
func EndSession(uid int64, token string) {
410f65b Jakob Wakeling 2023-10-23 15:14:42
55
	SessionsMutex.Lock()
570144e Jakob Wakeling 2023-12-15 23:28:06
56
	util.Debugln("[goit.EndSession] SessionsMutex lock")
410f65b Jakob Wakeling 2023-10-23 15:14:42
57
	defer SessionsMutex.Unlock()
570144e Jakob Wakeling 2023-12-15 23:28:06
58
	defer util.Debugln("[goit.EndSession] SessionsMutex unlock")
410f65b Jakob Wakeling 2023-10-23 15:14:42
59
410f65b Jakob Wakeling 2023-10-23 15:14:42
60
	if Sessions[uid] == nil {
410f65b Jakob Wakeling 2023-10-23 15:14:42
61
		return
410f65b Jakob Wakeling 2023-10-23 15:14:42
62
	}
410f65b Jakob Wakeling 2023-10-23 15:14:42
63
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
64
	for i, t := range Sessions[uid] {
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
65
		if t.Token == token {
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
66
			Sessions[uid] = append(Sessions[uid][:i], Sessions[uid][i+1:]...)
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
67
			break
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
68
		}
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
69
	}
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
70
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
71
	if len(Sessions[uid]) == 0 {
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
72
		delete(Sessions, uid)
0893c1e Jakob Wakeling 2023-07-21 17:11:15
73
	}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
74
}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
75
410f65b Jakob Wakeling 2023-10-23 15:14:42
76
/* Cleanup expired user sessions. */
0893c1e Jakob Wakeling 2023-07-21 17:11:15
77
func CleanupSessions() {
410f65b Jakob Wakeling 2023-10-23 15:14:42
78
	var n int = 0
0893c1e Jakob Wakeling 2023-07-21 17:11:15
79
410f65b Jakob Wakeling 2023-10-23 15:14:42
80
	SessionsMutex.Lock()
570144e Jakob Wakeling 2023-12-15 23:28:06
81
	util.Debugln("[goit.CleanupSessions] SessionsMutex lock")
5166d87 Jakob Wakeling 2023-10-23 16:00:35
82
410f65b Jakob Wakeling 2023-10-23 15:14:42
83
	for uid, v := range Sessions {
410f65b Jakob Wakeling 2023-10-23 15:14:42
84
		var i = 0
410f65b Jakob Wakeling 2023-10-23 15:14:42
85
		for _, s := range v {
410f65b Jakob Wakeling 2023-10-23 15:14:42
86
			if s.Expiry.After(time.Now()) {
410f65b Jakob Wakeling 2023-10-23 15:14:42
87
				v[i] = s
410f65b Jakob Wakeling 2023-10-23 15:14:42
88
				i += 1
0893c1e Jakob Wakeling 2023-07-21 17:11:15
89
			}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
90
		}
410f65b Jakob Wakeling 2023-10-23 15:14:42
91
410f65b Jakob Wakeling 2023-10-23 15:14:42
92
		n += len(v) - i
410f65b Jakob Wakeling 2023-10-23 15:14:42
93
410f65b Jakob Wakeling 2023-10-23 15:14:42
94
		if i == 0 {
410f65b Jakob Wakeling 2023-10-23 15:14:42
95
			delete(Sessions, uid)
410f65b Jakob Wakeling 2023-10-23 15:14:42
96
		} else {
410f65b Jakob Wakeling 2023-10-23 15:14:42
97
			Sessions[uid] = v[:i]
410f65b Jakob Wakeling 2023-10-23 15:14:42
98
		}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
99
	}
5166d87 Jakob Wakeling 2023-10-23 16:00:35
100
410f65b Jakob Wakeling 2023-10-23 15:14:42
101
	SessionsMutex.Unlock()
570144e Jakob Wakeling 2023-12-15 23:28:06
102
	util.Debugln("[goit.CleanupSessions] SessionsMutex unlock")
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
103
0893c1e Jakob Wakeling 2023-07-21 17:11:15
104
	if n > 0 {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
105
		log.Println("[Cleanup] cleaned up", n, "expired sessions")
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
106
	}
0893c1e Jakob Wakeling 2023-07-21 17:11:15
107
}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
108
410f65b Jakob Wakeling 2023-10-23 15:14:42
109
/* Set a user session cookie. */
0893c1e Jakob Wakeling 2023-07-21 17:11:15
110
func SetSessionCookie(w http.ResponseWriter, uid int64, s Session) {
b804701 Jakob Wakeling 2023-11-27 23:52:28
111
	c := &http.Cookie{
b804701 Jakob Wakeling 2023-11-27 23:52:28
112
		Name: "session", Value: fmt.Sprint(uid) + "." + s.Token, Path: "/", Expires: s.Expiry,
b804701 Jakob Wakeling 2023-11-27 23:52:28
113
		Secure: util.If(Conf.UsesHttps, true, false), HttpOnly: true, SameSite: http.SameSiteLaxMode,
b804701 Jakob Wakeling 2023-11-27 23:52:28
114
	}
b804701 Jakob Wakeling 2023-11-27 23:52:28
115
52b258d Jakob Wakeling 2023-07-21 19:22:31
116
	if err := c.Valid(); err != nil {
52b258d Jakob Wakeling 2023-07-21 19:22:31
117
		log.Println("[Cookie]", err.Error())
52b258d Jakob Wakeling 2023-07-21 19:22:31
118
	}
52b258d Jakob Wakeling 2023-07-21 19:22:31
119
52b258d Jakob Wakeling 2023-07-21 19:22:31
120
	http.SetCookie(w, c)
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
121
}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
122
410f65b Jakob Wakeling 2023-10-23 15:14:42
123
/* Get a user session cookie if one is present. */
0893c1e Jakob Wakeling 2023-07-21 17:11:15
124
func GetSessionCookie(r *http.Request) (int64, Session) {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
125
	if c := util.Cookie(r, "session"); c != nil {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
126
		ss := strings.SplitN(c.Value, ".", 2)
0893c1e Jakob Wakeling 2023-07-21 17:11:15
127
		if len(ss) != 2 {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
128
			return -1, Session{}
0893c1e Jakob Wakeling 2023-07-21 17:11:15
129
		}
0893c1e Jakob Wakeling 2023-07-21 17:11:15
130
410f65b Jakob Wakeling 2023-10-23 15:14:42
131
		uid, err := strconv.ParseInt(ss[0], 10, 64)
0893c1e Jakob Wakeling 2023-07-21 17:11:15
132
		if err != nil {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
133
			return -1, Session{}
a0ba6ae Jakob Wakeling 2023-07-21 14:52:36
134
		}
a0ba6ae Jakob Wakeling 2023-07-21 14:52:36
135
410f65b Jakob Wakeling 2023-10-23 15:14:42
136
		SessionsMutex.Lock()
570144e Jakob Wakeling 2023-12-15 23:28:06
137
		util.Debugln("[goit.GetSessionCookie] SessionsMutex lock")
5166d87 Jakob Wakeling 2023-10-23 16:00:35
138
		defer SessionsMutex.Unlock()
570144e Jakob Wakeling 2023-12-15 23:28:06
139
		defer util.Debugln("[goit.GetSessionCookie] SessionsMutex unlock")
5166d87 Jakob Wakeling 2023-10-23 16:00:35
140
410f65b Jakob Wakeling 2023-10-23 15:14:42
141
		for i, s := range Sessions[uid] {
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
142
			if ss[1] == s.Token {
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
143
				if s != (Session{}) {
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
144
					s.Seen = time.Now()
410f65b Jakob Wakeling 2023-10-23 15:14:42
145
					Sessions[uid][i] = s
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
146
				}
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
147
410f65b Jakob Wakeling 2023-10-23 15:14:42
148
				return uid, s
6fb9830 Jakob Wakeling 2023-09-28 18:04:37
149
			}
52b258d Jakob Wakeling 2023-07-21 19:22:31
150
		}
52b258d Jakob Wakeling 2023-07-21 19:22:31
151
410f65b Jakob Wakeling 2023-10-23 15:14:42
152
		return uid, Session{}
a0ba6ae Jakob Wakeling 2023-07-21 14:52:36
153
	}
a0ba6ae Jakob Wakeling 2023-07-21 14:52:36
154
0893c1e Jakob Wakeling 2023-07-21 17:11:15
155
	return -1, Session{}
0893c1e Jakob Wakeling 2023-07-21 17:11:15
156
}
0893c1e Jakob Wakeling 2023-07-21 17:11:15
157
410f65b Jakob Wakeling 2023-10-23 15:14:42
158
/* End the current user session cookie. */
0893c1e Jakob Wakeling 2023-07-21 17:11:15
159
func EndSessionCookie(w http.ResponseWriter) {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
160
	http.SetCookie(w, &http.Cookie{Name: "session", Path: "/", MaxAge: -1})
a0ba6ae Jakob Wakeling 2023-07-21 14:52:36
161
}
a0ba6ae Jakob Wakeling 2023-07-21 14:52:36
162
7974d70 Jakob Wakeling 2023-11-03 22:19:52
163
/* Authenticate a user session, returns auth, user, error. */
7974d70 Jakob Wakeling 2023-11-03 22:19:52
164
func Auth(w http.ResponseWriter, r *http.Request, renew bool) (bool, *User, error) {
7974d70 Jakob Wakeling 2023-11-03 22:19:52
165
	uid, s := GetSessionCookie(r)
7974d70 Jakob Wakeling 2023-11-03 22:19:52
166
	if s == (Session{}) {
7974d70 Jakob Wakeling 2023-11-03 22:19:52
167
		return false, nil, nil
7974d70 Jakob Wakeling 2023-11-03 22:19:52
168
	}
7974d70 Jakob Wakeling 2023-11-03 22:19:52
169
7974d70 Jakob Wakeling 2023-11-03 22:19:52
170
	/* Attempt to get the user associated with the session UID */
7974d70 Jakob Wakeling 2023-11-03 22:19:52
171
	user, err := GetUser(uid)
7974d70 Jakob Wakeling 2023-11-03 22:19:52
172
	if err != nil {
7974d70 Jakob Wakeling 2023-11-03 22:19:52
173
		return false, nil, fmt.Errorf("[auth] %w", err)
7974d70 Jakob Wakeling 2023-11-03 22:19:52
174
	}
7974d70 Jakob Wakeling 2023-11-03 22:19:52
175
7974d70 Jakob Wakeling 2023-11-03 22:19:52
176
	/* End invalid and expired sessions */
7974d70 Jakob Wakeling 2023-11-03 22:19:52
177
	if user == nil || s.Expiry.Before(time.Now()) {
7974d70 Jakob Wakeling 2023-11-03 22:19:52
178
		EndSession(uid, s.Token)
7974d70 Jakob Wakeling 2023-11-03 22:19:52
179
		return false, nil, nil
7974d70 Jakob Wakeling 2023-11-03 22:19:52
180
	}
7974d70 Jakob Wakeling 2023-11-03 22:19:52
181
7974d70 Jakob Wakeling 2023-11-03 22:19:52
182
	/* Renew the session if appropriate */
7974d70 Jakob Wakeling 2023-11-03 22:19:52
183
	if renew && time.Until(s.Expiry) < 24*time.Hour {
e530f2c Jakob Wakeling 2023-12-17 22:28:16
184
		ip := Ip(r)
e530f2c Jakob Wakeling 2023-12-17 22:28:16
185
7974d70 Jakob Wakeling 2023-11-03 22:19:52
186
		s1, err := NewSession(uid, ip, time.Now().Add(2*24*time.Hour))
7974d70 Jakob Wakeling 2023-11-03 22:19:52
187
		if err != nil {
7974d70 Jakob Wakeling 2023-11-03 22:19:52
188
			log.Println("[auth/renew]", err.Error())
7974d70 Jakob Wakeling 2023-11-03 22:19:52
189
		} else {
7974d70 Jakob Wakeling 2023-11-03 22:19:52
190
			SetSessionCookie(w, uid, s1)
7974d70 Jakob Wakeling 2023-11-03 22:19:52
191
			EndSession(uid, s.Token)
7974d70 Jakob Wakeling 2023-11-03 22:19:52
192
		}
7974d70 Jakob Wakeling 2023-11-03 22:19:52
193
	}
7974d70 Jakob Wakeling 2023-11-03 22:19:52
194
7974d70 Jakob Wakeling 2023-11-03 22:19:52
195
	return true, user, nil
7974d70 Jakob Wakeling 2023-11-03 22:19:52
196
}
7974d70 Jakob Wakeling 2023-11-03 22:19:52
197
0893c1e Jakob Wakeling 2023-07-21 17:11:15
198
/* Hash a password with a salt using Argon2. */
0893c1e Jakob Wakeling 2023-07-21 17:11:15
199
func Hash(pass string, salt []byte) []byte {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
200
	return argon2.IDKey([]byte(pass), salt, 3, 64*1024, 4, 32)
0893c1e Jakob Wakeling 2023-07-21 17:11:15
201
}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
202
0893c1e Jakob Wakeling 2023-07-21 17:11:15
203
/* Generate a random Base64 salt. */
0893c1e Jakob Wakeling 2023-07-21 17:11:15
204
func Salt() ([]byte, error) {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
205
	b := make([]byte, 16)
0893c1e Jakob Wakeling 2023-07-21 17:11:15
206
	if _, err := rand.Read(b); err != nil {
0893c1e Jakob Wakeling 2023-07-21 17:11:15
207
		return nil, err
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
208
	}
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
209
0893c1e Jakob Wakeling 2023-07-21 17:11:15
210
	return b, nil
ae5fc19 Jakob Wakeling 2023-07-17 21:54:54
211
}
212