Commit c58446c7 authored by SlevinWasAlreadyTaken's avatar SlevinWasAlreadyTaken
Browse files

feat: add ajwt acr concept, improve psql bubble, add merror acr origin

parent 320bb510
......@@ -17,20 +17,21 @@ type AppRole struct {
// Claims declared to format our Access JWTs
// Implement https://godoc.org/github.com/dgrijalva/jwt-go#Claims interface
type AccessClaims struct {
Issuer string `json:"iss"`
Audiences []string `json:"aud"`
ExpiresAt int64 `json:"exp"`
IssuedAt int64 `json:"iat"`
NotBefore int64 `json:"nbf"`
Subject string `json:"sub"`
Scope string `json:"sco"`
JWT string `json:"-"`
// /!\ deprecation in progress, to not enhance (use now role in scopes)
// formatted as `app_id_1:role_id_1 app_id_2:role_id_2`
Role string `json:"rol"`
ClientID string `json:"cli"`
Token string `json:"tok"`
Issuer string `json:"iss"` // Service which distributed the token
Audiences []string `json:"aud"` // Audiences which should answer to the tooken
ClientID string `json:"cli"` // SSO client ID which generated the token
ExpiresAt int64 `json:"exp"` // Expiry time
IssuedAt int64 `json:"iat"` // Issuing time
NotBefore int64 `json:"nbf"` // Time before use
Subject string `json:"sub"` // Subject (owner) bound to the token
Scope string `json:"sco"` // Scope beared by the token
ACR ACRSecLvl `json:"acr"` // Authentication Class Reference
Token string `json:"tok"` // Raw Access Token
JWT string `json:"-"` // Raw JWT Token
}
// setRawJWT set the raw jwt corresponding to the access claims
......
package ajwt
import "gitlab.com/Misakey/msk-sdk-go/merror"
// Security Level - Authentication Context Class Reference (a.k.a. ACR values)
// Corresponding to a level of certainty, while the token has been generated,
// of the identity of the token requester
// The higher the security level is, stronger is the possibility the subject claims
// correspond in reality to the person who generated an access token
type ACRSecLvl string
func (s ACRSecLvl) String() string {
return string(s)
}
var (
noSecLevel ACRSecLvl = "0"
ACRSecLvl1 ACRSecLvl = "1"
ACRSecLvl2 ACRSecLvl = "2"
)
// ACRGTE compares AccessClaims ACR (Authentication Context Class - Sec Level) with the secLvl parameter
// It uses kind of >= operator principle and return an error if the current ACR is stricty inferior to asked one
func (c *AccessClaims) ACRGTE(secLvl ACRSecLvl) error {
if c.ACR == noSecLevel {
return merror.Forbidden().From(merror.OriACR).
Describe("token acr is 0").
Detail("acr", merror.DVForbidden).Detail("required_acr", secLvl.String())
}
// highest sec level is by definition always greater than other or equal
if c.ACR == ACRSecLvl2 {
return nil
}
// lowest sec level is by definition always smaller than other so always valid
if secLvl == ACRSecLvl1 {
return nil
}
// there is no other combinaison to check today
return merror.Forbidden().From(merror.OriACR).
Describe("token acr is too weak").
Detail("acr", merror.DVForbidden).Detail("required_acr", secLvl.String())
}
package ajwt
import (
"testing"
"github.com/stretchr/testify/assert"
"gitlab.com/Misakey/msk-sdk-go/merror"
)
func TestACRGTE(t *testing.T) {
tests := map[string]struct {
claims AccessClaims
secLvl ACRSecLvl
err error
}{
"wished SecLevel 1 vs claim ACR 0 should return an error": {
claims: AccessClaims{
ACR: noSecLevel,
},
secLvl: ACRSecLvl1,
err: merror.Forbidden().From(merror.OriACR).Describe("token acr is 0").Detail("acr", merror.DVForbidden).Detail("required_acr", "1"),
},
"wished SecLevel 2 vs claim ACR 0 should return an error": {
claims: AccessClaims{
ACR: noSecLevel,
},
secLvl: ACRSecLvl2,
err: merror.Forbidden().From(merror.OriACR).Describe("token acr is 0").Detail("acr", merror.DVForbidden).Detail("required_acr", "2"),
},
"wished SecLevel 1 vs claim ACR 2 should return no error": {
claims: AccessClaims{
ACR: ACRSecLvl2,
},
secLvl: ACRSecLvl1,
err: nil,
},
"wished SecLevel 2 vs claim ACR 2 should return an error": {
claims: AccessClaims{
ACR: ACRSecLvl2,
},
secLvl: ACRSecLvl2,
err: nil,
},
"wished SecLevel 1 vs claim ACR 1 should return no error": {
claims: AccessClaims{
ACR: ACRSecLvl1,
},
secLvl: ACRSecLvl1,
err: nil,
},
"wished SecLevel 2 vs claim ACR 1 should return an error": {
claims: AccessClaims{
ACR: ACRSecLvl1,
},
secLvl: ACRSecLvl2,
err: merror.Forbidden().From(merror.OriACR).Describe("token acr is too weak").Detail("acr", merror.DVForbidden).Detail("required_acr", "2"),
},
}
for description, test := range tests {
t.Run(description, func(t *testing.T) {
result := test.claims.ACRGTE(test.secLvl)
assert.Equal(t, test.err, result)
})
}
}
......@@ -21,19 +21,16 @@ func (n PSQLNeedle) Explode(err error) error {
return nil
}
if pqErr.Code.Name() == "foreign_key" ||
pqErr.Code.Name() == "unique_violation" ||
pqErr.Code.Name() == "foreign_key_violation" {
switch pqErr.Code.Name() {
case "foreign_key", "unique_violation", "foreign_key_violation":
return merror.Conflict().Describe(err.Error())
}
if pqErr.Code.Name() == "invalid_text_representation" {
case "invalid_text_representation", "not_null_violation":
return merror.BadRequest().Describe(err.Error())
}
if pqErr.Code.Name() == "string_data_right_truncation" {
case "string_data_right_truncation":
return merror.RequestEntityTooLarge().Describe(err.Error())
}
if pqErr.Code.Name() == "query_canceled" {
case "query_canceled":
return merror.ClientClosedRequest().Describe(err.Error())
}
return err
}
package bubble
import (
"testing"
"github.com/lib/pq"
"github.com/stretchr/testify/assert"
"gitlab.com/Misakey/msk-sdk-go/merror"
)
func TestACRGTE(t *testing.T) {
tests := map[string]struct {
inputErr error
expectedErr error
}{
"foreign_key pq error shall return a conflict merror": {
inputErr: &pq.Error{Code: "23503", Message: "dummy error"},
expectedErr: merror.Conflict().Describe("pq: dummy error"),
},
"unique_violation pq error shall return a conflict merror": {
inputErr: &pq.Error{Code: "23505", Message: "dummy error"},
expectedErr: merror.Conflict().Describe("pq: dummy error"),
},
"foreign_key_violation pq error shall return a conflict merror": {
inputErr: &pq.Error{Code: "23503", Message: "dummy error"},
expectedErr: merror.Conflict().Describe("pq: dummy error"),
},
"invalid_text_representation pq error shall return a bad request merror": {
inputErr: &pq.Error{Code: "22P02", Message: "dummy error"},
expectedErr: merror.BadRequest().Describe("pq: dummy error"),
},
"not_null_violation pq error shall return a bad request merror": {
inputErr: &pq.Error{Code: "23502", Message: "dummy error"},
expectedErr: merror.BadRequest().Describe("pq: dummy error"),
},
"string_data_right_truncation pq error shall return a entity too large merror": {
inputErr: &pq.Error{Code: "01004", Message: "dummy error"},
expectedErr: merror.RequestEntityTooLarge().Describe("pq: dummy error"),
},
"query_canceled pq error shall return a client closed request merror": {
inputErr: &pq.Error{Code: "57014", Message: "dummy error"},
expectedErr: merror.ClientClosedRequest().Describe("pq: dummy error"),
},
}
for description, test := range tests {
t.Run(description, func(t *testing.T) {
result := PSQLNeedle{}.Explode(test.inputErr)
assert.Equal(t, test.expectedErr, result)
})
}
}
......@@ -5,11 +5,12 @@ package merror
type Origin string
const (
OriACR Origin = "acr" // the error comes from the authorization token acr
OriBody Origin = "body" // the error comes from body parameter
OriQuery Origin = "query" // the error comes from query parameters
OriPath Origin = "path" // the error comes from path parameters
OriHeaders Origin = "headers" // the error comes from headers
OriInternal Origin = "internal" // the error comes from internal logic
OriQuery Origin = "query" // the error comes from query parameters
OriPath Origin = "path" // the error comes from path parameters
OriNotDefined Origin = "not_defined" // the error has no origin defined yet
)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment