24904585a2
Fix user tests Add swagger docs Fix lint Add totp check when logging in Make totp enrollment work Add migration for totp table go mod vendor Add routes for totp routes Add route handler for totp routes Add basic implementation to enroll a user in totp Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/api/pulls/383
166 lines
3.2 KiB
Go
166 lines
3.2 KiB
Go
package qr
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
"math"
|
|
|
|
"github.com/boombuler/barcode"
|
|
"github.com/boombuler/barcode/utils"
|
|
)
|
|
|
|
type qrcode struct {
|
|
dimension int
|
|
data *utils.BitList
|
|
content string
|
|
}
|
|
|
|
func (qr *qrcode) Content() string {
|
|
return qr.content
|
|
}
|
|
|
|
func (qr *qrcode) Metadata() barcode.Metadata {
|
|
return barcode.Metadata{barcode.TypeQR, 2}
|
|
}
|
|
|
|
func (qr *qrcode) ColorModel() color.Model {
|
|
return color.Gray16Model
|
|
}
|
|
|
|
func (qr *qrcode) Bounds() image.Rectangle {
|
|
return image.Rect(0, 0, qr.dimension, qr.dimension)
|
|
}
|
|
|
|
func (qr *qrcode) At(x, y int) color.Color {
|
|
if qr.Get(x, y) {
|
|
return color.Black
|
|
}
|
|
return color.White
|
|
}
|
|
|
|
func (qr *qrcode) Get(x, y int) bool {
|
|
return qr.data.GetBit(x*qr.dimension + y)
|
|
}
|
|
|
|
func (qr *qrcode) Set(x, y int, val bool) {
|
|
qr.data.SetBit(x*qr.dimension+y, val)
|
|
}
|
|
|
|
func (qr *qrcode) calcPenalty() uint {
|
|
return qr.calcPenaltyRule1() + qr.calcPenaltyRule2() + qr.calcPenaltyRule3() + qr.calcPenaltyRule4()
|
|
}
|
|
|
|
func (qr *qrcode) calcPenaltyRule1() uint {
|
|
var result uint
|
|
for x := 0; x < qr.dimension; x++ {
|
|
checkForX := false
|
|
var cntX uint
|
|
checkForY := false
|
|
var cntY uint
|
|
|
|
for y := 0; y < qr.dimension; y++ {
|
|
if qr.Get(x, y) == checkForX {
|
|
cntX++
|
|
} else {
|
|
checkForX = !checkForX
|
|
if cntX >= 5 {
|
|
result += cntX - 2
|
|
}
|
|
cntX = 1
|
|
}
|
|
|
|
if qr.Get(y, x) == checkForY {
|
|
cntY++
|
|
} else {
|
|
checkForY = !checkForY
|
|
if cntY >= 5 {
|
|
result += cntY - 2
|
|
}
|
|
cntY = 1
|
|
}
|
|
}
|
|
|
|
if cntX >= 5 {
|
|
result += cntX - 2
|
|
}
|
|
if cntY >= 5 {
|
|
result += cntY - 2
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (qr *qrcode) calcPenaltyRule2() uint {
|
|
var result uint
|
|
for x := 0; x < qr.dimension-1; x++ {
|
|
for y := 0; y < qr.dimension-1; y++ {
|
|
check := qr.Get(x, y)
|
|
if qr.Get(x, y+1) == check && qr.Get(x+1, y) == check && qr.Get(x+1, y+1) == check {
|
|
result += 3
|
|
}
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (qr *qrcode) calcPenaltyRule3() uint {
|
|
pattern1 := []bool{true, false, true, true, true, false, true, false, false, false, false}
|
|
pattern2 := []bool{false, false, false, false, true, false, true, true, true, false, true}
|
|
|
|
var result uint
|
|
for x := 0; x <= qr.dimension-len(pattern1); x++ {
|
|
for y := 0; y < qr.dimension; y++ {
|
|
pattern1XFound := true
|
|
pattern2XFound := true
|
|
pattern1YFound := true
|
|
pattern2YFound := true
|
|
|
|
for i := 0; i < len(pattern1); i++ {
|
|
iv := qr.Get(x+i, y)
|
|
if iv != pattern1[i] {
|
|
pattern1XFound = false
|
|
}
|
|
if iv != pattern2[i] {
|
|
pattern2XFound = false
|
|
}
|
|
iv = qr.Get(y, x+i)
|
|
if iv != pattern1[i] {
|
|
pattern1YFound = false
|
|
}
|
|
if iv != pattern2[i] {
|
|
pattern2YFound = false
|
|
}
|
|
}
|
|
if pattern1XFound || pattern2XFound {
|
|
result += 40
|
|
}
|
|
if pattern1YFound || pattern2YFound {
|
|
result += 40
|
|
}
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (qr *qrcode) calcPenaltyRule4() uint {
|
|
totalNum := qr.data.Len()
|
|
trueCnt := 0
|
|
for i := 0; i < totalNum; i++ {
|
|
if qr.data.GetBit(i) {
|
|
trueCnt++
|
|
}
|
|
}
|
|
percDark := float64(trueCnt) * 100 / float64(totalNum)
|
|
floor := math.Abs(math.Floor(percDark/5) - 10)
|
|
ceil := math.Abs(math.Ceil(percDark/5) - 10)
|
|
return uint(math.Min(floor, ceil) * 10)
|
|
}
|
|
|
|
func newBarcode(dim int) *qrcode {
|
|
res := new(qrcode)
|
|
res.dimension = dim
|
|
res.data = utils.NewBitList(dim * dim)
|
|
return res
|
|
}
|