parent
30c963a5f0
commit
61a1c93f0f
89 changed files with 15859 additions and 2 deletions
102
server/libs/walls/access_walls.go
Normal file
102
server/libs/walls/access_walls.go
Normal file
|
@ -0,0 +1,102 @@
|
|||
package walls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
import (
|
||||
"quickshare/server/libs/cfg"
|
||||
"quickshare/server/libs/encrypt"
|
||||
"quickshare/server/libs/limiter"
|
||||
)
|
||||
|
||||
type AccessWalls struct {
|
||||
cf *cfg.Config
|
||||
IpLimiter limiter.Limiter
|
||||
OpLimiter limiter.Limiter
|
||||
EncrypterMaker encrypt.EncrypterMaker
|
||||
}
|
||||
|
||||
func NewAccessWalls(
|
||||
cf *cfg.Config,
|
||||
ipLimiter limiter.Limiter,
|
||||
opLimiter limiter.Limiter,
|
||||
encrypterMaker encrypt.EncrypterMaker,
|
||||
) Walls {
|
||||
return &AccessWalls{
|
||||
cf: cf,
|
||||
IpLimiter: ipLimiter,
|
||||
OpLimiter: opLimiter,
|
||||
EncrypterMaker: encrypterMaker,
|
||||
}
|
||||
}
|
||||
|
||||
func (walls *AccessWalls) PassIpLimit(remoteAddr string) bool {
|
||||
if !walls.cf.Production {
|
||||
return true
|
||||
}
|
||||
return walls.IpLimiter.Access(remoteAddr, walls.cf.OpIdIpVisit)
|
||||
|
||||
}
|
||||
|
||||
func (walls *AccessWalls) PassOpLimit(resourceId string, opId int16) bool {
|
||||
if !walls.cf.Production {
|
||||
return true
|
||||
}
|
||||
return walls.OpLimiter.Access(resourceId, opId)
|
||||
}
|
||||
|
||||
func (walls *AccessWalls) PassLoginCheck(tokenStr string, req *http.Request) bool {
|
||||
if !walls.cf.Production {
|
||||
return true
|
||||
}
|
||||
|
||||
return walls.passLoginCheck(tokenStr)
|
||||
}
|
||||
|
||||
func (walls *AccessWalls) passLoginCheck(tokenStr string) bool {
|
||||
token, getLoginTokenOk := walls.GetLoginToken(tokenStr)
|
||||
return getLoginTokenOk && token.AdminId == walls.cf.AdminId
|
||||
}
|
||||
|
||||
func (walls *AccessWalls) GetLoginToken(tokenStr string) (*LoginToken, bool) {
|
||||
tokenMaker := walls.EncrypterMaker(string(walls.cf.SecretKeyByte))
|
||||
if !tokenMaker.FromStr(tokenStr) {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
adminIdFromToken, adminIdOk := tokenMaker.Get(walls.cf.KeyAdminId)
|
||||
expiresStr, expiresStrOk := tokenMaker.Get(walls.cf.KeyExpires)
|
||||
if !adminIdOk || !expiresStrOk {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
expires, expiresParseErr := strconv.ParseInt(expiresStr, 10, 64)
|
||||
if expiresParseErr != nil ||
|
||||
adminIdFromToken != walls.cf.AdminId ||
|
||||
expires <= time.Now().Unix() {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
return &LoginToken{
|
||||
AdminId: adminIdFromToken,
|
||||
Expires: expires,
|
||||
}, true
|
||||
}
|
||||
|
||||
func (walls *AccessWalls) MakeLoginToken(userId string) string {
|
||||
expires := time.Now().Add(time.Duration(walls.cf.CookieMaxAge) * time.Second).Unix()
|
||||
|
||||
tokenMaker := walls.EncrypterMaker(string(walls.cf.SecretKeyByte))
|
||||
tokenMaker.Add(walls.cf.KeyAdminId, userId)
|
||||
tokenMaker.Add(walls.cf.KeyExpires, fmt.Sprintf("%d", expires))
|
||||
|
||||
tokenStr, ok := tokenMaker.ToStr()
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
return tokenStr
|
||||
}
|
145
server/libs/walls/access_walls_test.go
Normal file
145
server/libs/walls/access_walls_test.go
Normal file
|
@ -0,0 +1,145 @@
|
|||
package walls
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
import (
|
||||
"quickshare/server/libs/cfg"
|
||||
"quickshare/server/libs/encrypt"
|
||||
"quickshare/server/libs/limiter"
|
||||
)
|
||||
|
||||
func newAccessWalls(limiterCap int64, limiterTtl int32, limiterCyc int32, bucketCap int16) *AccessWalls {
|
||||
config := cfg.NewConfig()
|
||||
config.Production = true
|
||||
config.LimiterCap = limiterCap
|
||||
config.LimiterTtl = limiterTtl
|
||||
config.LimiterCyc = limiterCyc
|
||||
config.BucketCap = bucketCap
|
||||
encrypterMaker := encrypt.JwtEncrypterMaker
|
||||
ipLimiter := limiter.NewRateLimiter(config.LimiterCap, config.LimiterTtl, config.LimiterCyc, config.BucketCap, map[int16]int16{})
|
||||
opLimiter := limiter.NewRateLimiter(config.LimiterCap, config.LimiterTtl, config.LimiterCyc, config.BucketCap, map[int16]int16{})
|
||||
|
||||
return NewAccessWalls(config, ipLimiter, opLimiter, encrypterMaker).(*AccessWalls)
|
||||
}
|
||||
func TestIpLimit(t *testing.T) {
|
||||
ip := "0.0.0.0"
|
||||
limit := int16(10)
|
||||
ttl := int32(60)
|
||||
cyc := int32(5)
|
||||
walls := newAccessWalls(1000, ttl, cyc, limit)
|
||||
|
||||
testIpLimit(t, walls, ip, limit)
|
||||
// wait for tokens are re-fullfilled
|
||||
time.Sleep(time.Duration(cyc) * time.Second)
|
||||
testIpLimit(t, walls, ip, limit)
|
||||
|
||||
fmt.Println("ip limit: passed")
|
||||
}
|
||||
|
||||
func testIpLimit(t *testing.T, walls Walls, ip string, limit int16) {
|
||||
for i := int16(0); i < limit; i++ {
|
||||
if !walls.PassIpLimit(ip) {
|
||||
t.Fatalf("ipLimiter: should be passed", time.Now().Unix())
|
||||
}
|
||||
}
|
||||
|
||||
if walls.PassIpLimit(ip) {
|
||||
t.Fatalf("ipLimiter: should not be passed", time.Now().Unix())
|
||||
}
|
||||
}
|
||||
|
||||
func TestOpLimit(t *testing.T) {
|
||||
resourceId := "id"
|
||||
op1 := int16(1)
|
||||
op2 := int16(2)
|
||||
limit := int16(10)
|
||||
ttl := int32(1)
|
||||
walls := newAccessWalls(1000, 5, ttl, limit)
|
||||
|
||||
testOpLimit(t, walls, resourceId, op1, limit)
|
||||
testOpLimit(t, walls, resourceId, op2, limit)
|
||||
// wait for tokens are re-fullfilled
|
||||
time.Sleep(time.Duration(ttl) * time.Second)
|
||||
testOpLimit(t, walls, resourceId, op1, limit)
|
||||
testOpLimit(t, walls, resourceId, op2, limit)
|
||||
|
||||
fmt.Println("op limit: passed")
|
||||
}
|
||||
|
||||
func testOpLimit(t *testing.T, walls Walls, resourceId string, op int16, limit int16) {
|
||||
for i := int16(0); i < limit; i++ {
|
||||
if !walls.PassOpLimit(resourceId, op) {
|
||||
t.Fatalf("opLimiter: should be passed")
|
||||
}
|
||||
}
|
||||
|
||||
if walls.PassOpLimit(resourceId, op) {
|
||||
t.Fatalf("opLimiter: should not be passed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginCheck(t *testing.T) {
|
||||
walls := newAccessWalls(1000, 5, 1, 10)
|
||||
|
||||
testValidToken(t, walls)
|
||||
testInvalidAdminIdToken(t, walls)
|
||||
testExpiredToken(t, walls)
|
||||
}
|
||||
|
||||
func testValidToken(t *testing.T, walls *AccessWalls) {
|
||||
config := cfg.NewConfig()
|
||||
|
||||
tokenMaker := encrypt.JwtEncrypterMaker(string(config.SecretKeyByte))
|
||||
tokenMaker.Add(config.KeyAdminId, config.AdminId)
|
||||
tokenMaker.Add(config.KeyExpires, fmt.Sprintf("%d", time.Now().Unix()+int64(10)))
|
||||
tokenStr, getTokenOk := tokenMaker.ToStr()
|
||||
if !getTokenOk {
|
||||
t.Fatalf("passLoginCheck: fail to generate token")
|
||||
}
|
||||
|
||||
if !walls.passLoginCheck(tokenStr) {
|
||||
t.Fatalf("loginCheck: should be passed")
|
||||
}
|
||||
|
||||
fmt.Println("loginCheck: valid token passed")
|
||||
}
|
||||
|
||||
func testInvalidAdminIdToken(t *testing.T, walls *AccessWalls) {
|
||||
config := cfg.NewConfig()
|
||||
|
||||
tokenMaker := encrypt.JwtEncrypterMaker(string(config.SecretKeyByte))
|
||||
tokenMaker.Add(config.KeyAdminId, "invalid admin id")
|
||||
tokenMaker.Add(config.KeyExpires, fmt.Sprintf("%d", time.Now().Unix()+int64(10)))
|
||||
tokenStr, getTokenOk := tokenMaker.ToStr()
|
||||
if !getTokenOk {
|
||||
t.Fatalf("passLoginCheck: fail to generate token")
|
||||
}
|
||||
|
||||
if walls.passLoginCheck(tokenStr) {
|
||||
t.Fatalf("loginCheck: should not be passed")
|
||||
}
|
||||
|
||||
fmt.Println("loginCheck: invalid admin id passed")
|
||||
}
|
||||
|
||||
func testExpiredToken(t *testing.T, walls *AccessWalls) {
|
||||
config := cfg.NewConfig()
|
||||
|
||||
tokenMaker := encrypt.JwtEncrypterMaker(string(config.SecretKeyByte))
|
||||
tokenMaker.Add(config.KeyAdminId, config.AdminId)
|
||||
tokenMaker.Add(config.KeyExpires, fmt.Sprintf("%d", time.Now().Unix()-int64(1)))
|
||||
tokenStr, getTokenOk := tokenMaker.ToStr()
|
||||
if !getTokenOk {
|
||||
t.Fatalf("passLoginCheck: fail to generate token")
|
||||
}
|
||||
|
||||
if walls.passLoginCheck(tokenStr) {
|
||||
t.Fatalf("loginCheck: should not be passed")
|
||||
}
|
||||
|
||||
fmt.Println("loginCheck: expired token passed")
|
||||
}
|
17
server/libs/walls/walls.go
Normal file
17
server/libs/walls/walls.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
package walls
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type Walls interface {
|
||||
PassIpLimit(remoteAddr string) bool
|
||||
PassOpLimit(resourceId string, opId int16) bool
|
||||
PassLoginCheck(tokenStr string, req *http.Request) bool
|
||||
MakeLoginToken(uid string) string
|
||||
}
|
||||
|
||||
type LoginToken struct {
|
||||
AdminId string
|
||||
Expires int64
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue