Initial
This commit is contained in:
102
internal/client/fetch_status.go
Normal file
102
internal/client/fetch_status.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"netisjwt/internal/jws"
|
||||
)
|
||||
|
||||
// PridobiInPreveriStatus poklice GET endpoint, preveri JWS in vrne status.
|
||||
func PridobiInPreveriStatus(ctx context.Context, targetURL string) (bool, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, targetURL, nil)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
rawToken, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
token := strings.TrimSpace(string(rawToken))
|
||||
|
||||
claims, err := jws.PreveriZeton(token)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if err := jws.PreveriTrditve(claims, izdajateljIzURL(targetURL), time.Now()); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
values, err := dekodirajKodiranSeznam(claims.Status.EncodedList)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return preberiBit(values, claims.Status.Index)
|
||||
}
|
||||
|
||||
// izdajateljIzURL vrne url brez zadnjega segmenta index.
|
||||
func izdajateljIzURL(url string) string {
|
||||
parts := strings.Split(url, "/")
|
||||
if len(parts) < 5 {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(parts[:len(parts)-1], "/")
|
||||
}
|
||||
|
||||
// dekodirajKodiranSeznam pretvori base64+gzip seznam v surove bajte.
|
||||
func dekodirajKodiranSeznam(encoded string) ([]byte, error) {
|
||||
compressed, err := base64.StdEncoding.DecodeString(encoded)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reader, err := newGzipBralnik(compressed)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reader.Close()
|
||||
|
||||
return io.ReadAll(reader)
|
||||
}
|
||||
|
||||
// preberiBit prebere en bit iz podanega bajtnega zaporedja.
|
||||
func preberiBit(data []byte, index int) (bool, error) {
|
||||
if index < 0 {
|
||||
return false, errors.New("index must be >= 0")
|
||||
}
|
||||
byteIdx := index / 8
|
||||
if byteIdx >= len(data) {
|
||||
return false, errors.New("index is out of encoded list range")
|
||||
}
|
||||
mask := byte(1 << (index % 8))
|
||||
return data[byteIdx]&mask != 0, nil
|
||||
}
|
||||
|
||||
type gzipReader interface {
|
||||
io.Reader
|
||||
Close() error
|
||||
}
|
||||
|
||||
// newGzipBralnik ustvari gzip reader iz podanih bajtov.
|
||||
func newGzipBralnik(input []byte) (gzipReader, error) {
|
||||
return gzip.NewReader(bytes.NewReader(input))
|
||||
}
|
||||
48
internal/client/fetch_status_test.go
Normal file
48
internal/client/fetch_status_test.go
Normal file
@@ -0,0 +1,48 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
appcrypto "netisjwt/internal/crypto"
|
||||
"netisjwt/internal/jws"
|
||||
"netisjwt/internal/statuslist"
|
||||
)
|
||||
|
||||
func TestFetchAndVerifyStatus(t *testing.T) {
|
||||
privatePEM, err := appcrypto.UstvariZasebniKljucECDSAP256PEM()
|
||||
if err != nil {
|
||||
t.Fatalf("key gen: %v", err)
|
||||
}
|
||||
|
||||
list := statuslist.New()
|
||||
list.Dodaj(false)
|
||||
list.Dodaj(true)
|
||||
|
||||
tokenPath := "/api/status/demo/1"
|
||||
var baseURL string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
claims, err := jws.SestaviTrditve(baseURL+"/api/status/demo", list, 1, time.Now())
|
||||
if err != nil {
|
||||
t.Fatalf("build claims: %v", err)
|
||||
}
|
||||
token, err := jws.UstvariZetonIzPEM(privatePEM, claims)
|
||||
if err != nil {
|
||||
t.Fatalf("create token: %v", err)
|
||||
}
|
||||
_, _ = w.Write([]byte(token))
|
||||
}))
|
||||
defer server.Close()
|
||||
baseURL = server.URL
|
||||
|
||||
value, err := PridobiInPreveriStatus(context.Background(), server.URL+tokenPath)
|
||||
if err != nil {
|
||||
t.Fatalf("fetch and verify: %v", err)
|
||||
}
|
||||
if !value {
|
||||
t.Fatalf("expected true")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user