diff --git a/internal/endpoints/tokeninfo/history.go b/internal/endpoints/tokeninfo/history.go index 5ac80581fd1473dd82f2323461e4f97377f93034..40870a715bdfacaf34b43ca7212ace2757f7737c 100644 --- a/internal/endpoints/tokeninfo/history.go +++ b/internal/endpoints/tokeninfo/history.go @@ -3,7 +3,6 @@ package tokeninfo import ( "database/sql" - "github.com/gofiber/fiber/v2" "github.com/jmoiron/sqlx" "github.com/oidc-mytoken/api/v0" "github.com/pkg/errors" @@ -14,7 +13,6 @@ import ( response "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" "github.com/oidc-mytoken/server/internal/endpoints/tokeninfo/pkg" "github.com/oidc-mytoken/server/internal/model" - "github.com/oidc-mytoken/server/internal/utils/cookies" "github.com/oidc-mytoken/server/internal/utils/errorfmt" eventService "github.com/oidc-mytoken/server/shared/mytoken/event" event "github.com/oidc-mytoken/server/shared/mytoken/event/pkg" @@ -23,32 +21,8 @@ import ( "github.com/oidc-mytoken/server/shared/mytoken/rotation" ) -func handleTokenInfoHistory(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData) model.Response { - // If we call this function it means the token is valid. - - if !mt.Capabilities.Has(api.CapabilityTokeninfoHistory) { - return model.Response{ - Status: fiber.StatusForbidden, - Response: api.ErrorInsufficientCapabilities, - } - } - - var usedRestriction *restrictions.Restriction - if len(mt.Restrictions) > 0 { - possibleRestrictions := mt.Restrictions.GetValidForOther(nil, clientMetadata.IP, mt.ID) - if len(possibleRestrictions) == 0 { - return model.Response{ - Status: fiber.StatusForbidden, - Response: api.ErrorUsageRestricted, - } - } - usedRestriction = &possibleRestrictions[0] - } - - var history eventrepo.EventHistory - var tokenUpdate *response.MytokenResponse - if err := db.Transact(func(tx *sqlx.Tx) error { - var err error +func doTokenInfoHistory(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData, usedRestriction *restrictions.Restriction) (history eventrepo.EventHistory, tokenUpdate *response.MytokenResponse, err error) { + err = db.Transact(func(tx *sqlx.Tx) error { history, err = eventrepo.GetEventHistory(tx, mt.ID) if err != nil && !errors.Is(err, sql.ErrNoRows) { return err @@ -68,20 +42,21 @@ func handleTokenInfoHistory(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clien Event: event.FromNumber(event.MTEventTokenInfoHistory, ""), MTID: mt.ID, }, *clientMetadata) - }); err != nil { + }) + return +} + +func handleTokenInfoHistory(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData) model.Response { + // If we call this function it means the token is valid. + usedRestriction, errRes := checkTokenInfo(mt, clientMetadata, api.CapabilityTokeninfoHistory) + if errRes != nil { + return *errRes + } + history, tokenUpdate, err := doTokenInfoHistory(req, mt, clientMetadata, usedRestriction) + if err != nil { log.Errorf("%s", errorfmt.Full(err)) return *model.ErrorToInternalServerErrorResponse(err) } - rsp := pkg.NewTokeninfoHistoryResponse(history) - var cake []*fiber.Cookie - if tokenUpdate != nil { - rsp.TokenUpdate = tokenUpdate - cookie := cookies.MytokenCookie(tokenUpdate.Mytoken) - cake = []*fiber.Cookie{&cookie} - } - return model.Response{ - Status: fiber.StatusOK, - Response: rsp, - Cookies: cake, - } + rsp := pkg.NewTokeninfoHistoryResponse(history, tokenUpdate) + return makeTokenInfoResponse(rsp, tokenUpdate) } diff --git a/internal/endpoints/tokeninfo/introspect.go b/internal/endpoints/tokeninfo/introspect.go index 45754ca2188b0e85a43ecdbf00f8405c5d7501f9..f59b39e855ca02590927abe5b4bf16f2e82cef98 100644 --- a/internal/endpoints/tokeninfo/introspect.go +++ b/internal/endpoints/tokeninfo/introspect.go @@ -18,13 +18,13 @@ import ( func handleTokenInfoIntrospect(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData) model.Response { // If we call this function it means the token is valid. - if !mt.Capabilities.Has(api.CapabilityTokeninfoIntrospect) { return model.Response{ Status: fiber.StatusForbidden, Response: api.ErrorInsufficientCapabilities, } } + var usedToken mytoken.UsedMytoken if err := db.RunWithinTransaction(nil, func(tx *sqlx.Tx) error { tmp, err := mt.ToUsedMytoken(tx) diff --git a/internal/endpoints/tokeninfo/list.go b/internal/endpoints/tokeninfo/list.go index a5309b8fed47d58444bd5e812ddf2bf12c1c2bda..158961d7ec5895ff6b1a7555d79df799d09c4ed7 100644 --- a/internal/endpoints/tokeninfo/list.go +++ b/internal/endpoints/tokeninfo/list.go @@ -3,18 +3,17 @@ package tokeninfo import ( "database/sql" - "github.com/gofiber/fiber/v2" "github.com/jmoiron/sqlx" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "github.com/oidc-mytoken/api/v0" + "github.com/oidc-mytoken/server/internal/db" "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/tree" response "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" "github.com/oidc-mytoken/server/internal/endpoints/tokeninfo/pkg" "github.com/oidc-mytoken/server/internal/model" - "github.com/oidc-mytoken/server/internal/utils/cookies" "github.com/oidc-mytoken/server/internal/utils/errorfmt" eventService "github.com/oidc-mytoken/server/shared/mytoken/event" event "github.com/oidc-mytoken/server/shared/mytoken/event/pkg" @@ -23,32 +22,8 @@ import ( "github.com/oidc-mytoken/server/shared/mytoken/rotation" ) -func handleTokenInfoList(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData) model.Response { - // If we call this function it means the token is valid. - - if !mt.Capabilities.Has(api.CapabilityListMT) { - return model.Response{ - Status: fiber.StatusForbidden, - Response: api.ErrorInsufficientCapabilities, - } - } - - var usedRestriction *restrictions.Restriction - if len(mt.Restrictions) > 0 { - possibleRestrictions := mt.Restrictions.GetValidForOther(nil, clientMetadata.IP, mt.ID) - if len(possibleRestrictions) == 0 { - return model.Response{ - Status: fiber.StatusForbidden, - Response: api.ErrorUsageRestricted, - } - } - usedRestriction = &possibleRestrictions[0] - } - - var tokenList []tree.MytokenEntryTree - var tokenUpdate *response.MytokenResponse - if err := db.Transact(func(tx *sqlx.Tx) error { - var err error +func doTokenInfoList(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData, usedRestriction *restrictions.Restriction) (tokenList []tree.MytokenEntryTree, tokenUpdate *response.MytokenResponse, err error) { + err = db.Transact(func(tx *sqlx.Tx) error { tokenList, err = tree.AllTokens(tx, mt.ID) if err != nil && !errors.Is(err, sql.ErrNoRows) { return err @@ -68,21 +43,22 @@ func handleTokenInfoList(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMe Event: event.FromNumber(event.MTEventTokenInfoListMTs, ""), MTID: mt.ID, }, *clientMetadata) - }); err != nil { + }) + return +} + +func handleTokenInfoList(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData) model.Response { + // If we call this function it means the token is valid. + usedRestriction, errRes := checkTokenInfo(mt, clientMetadata, api.CapabilityListMT) + if errRes != nil { + return *errRes + } + tokenList, tokenUpdate, err := doTokenInfoList(req, mt, clientMetadata, usedRestriction) + if err != nil { log.Errorf("%s", errorfmt.Full(err)) return *model.ErrorToInternalServerErrorResponse(err) } + rsp := pkg.NewTokeninfoListResponse(tokenList, tokenUpdate) + return makeTokenInfoResponse(rsp, tokenUpdate) - rsp := pkg.NewTokeninfoListResponse(tokenList) - var cake []*fiber.Cookie - if tokenUpdate != nil { - rsp.TokenUpdate = tokenUpdate - cookie := cookies.MytokenCookie(tokenUpdate.Mytoken) - cake = []*fiber.Cookie{&cookie} - } - return model.Response{ - Status: fiber.StatusOK, - Response: rsp, - Cookies: cake, - } } diff --git a/internal/endpoints/tokeninfo/pkg/tokeninfoHistoryResponse.go b/internal/endpoints/tokeninfo/pkg/tokeninfoHistoryResponse.go index edd0f7cb3f8e6049e70abd6f2f0687cffcdaf510..449c919d1667f876b71c04a884fece5661b3f5b4 100644 --- a/internal/endpoints/tokeninfo/pkg/tokeninfoHistoryResponse.go +++ b/internal/endpoints/tokeninfo/pkg/tokeninfoHistoryResponse.go @@ -13,6 +13,6 @@ type TokeninfoHistoryResponse struct { } // NewTokeninfoHistoryResponse creates a new TokeninfoHistoryResponse -func NewTokeninfoHistoryResponse(h eventrepo.EventHistory) TokeninfoHistoryResponse { - return TokeninfoHistoryResponse{EventHistory: h} +func NewTokeninfoHistoryResponse(h eventrepo.EventHistory, update *my.MytokenResponse) TokeninfoHistoryResponse { + return TokeninfoHistoryResponse{EventHistory: h, TokenUpdate: update} } diff --git a/internal/endpoints/tokeninfo/pkg/tokeninfoListResponse.go b/internal/endpoints/tokeninfo/pkg/tokeninfoListResponse.go index 965af3f401eeec9cabcc899eb8274379679236c3..e16d1bb29242792b509d4c5ad8665e30d6729f05 100644 --- a/internal/endpoints/tokeninfo/pkg/tokeninfoListResponse.go +++ b/internal/endpoints/tokeninfo/pkg/tokeninfoListResponse.go @@ -13,6 +13,6 @@ type TokeninfoListResponse struct { } // NewTokeninfoListResponse creates a new TokeninfoListResponse -func NewTokeninfoListResponse(l []tree.MytokenEntryTree) TokeninfoListResponse { - return TokeninfoListResponse{Tokens: l} +func NewTokeninfoListResponse(l []tree.MytokenEntryTree, update *my.MytokenResponse) TokeninfoListResponse { + return TokeninfoListResponse{Tokens: l, TokenUpdate: update} } diff --git a/internal/endpoints/tokeninfo/pkg/tokeninfoTreeResponse.go b/internal/endpoints/tokeninfo/pkg/tokeninfoTreeResponse.go index c6907e19e7775c1f54e4000d332dce01bfd07e4e..322a552287f008e3ecb72ff9d8934771e8717f6d 100644 --- a/internal/endpoints/tokeninfo/pkg/tokeninfoTreeResponse.go +++ b/internal/endpoints/tokeninfo/pkg/tokeninfoTreeResponse.go @@ -13,6 +13,6 @@ type TokeninfoTreeResponse struct { } // NewTokeninfoTreeResponse creates a new TokeninfoTreeResponse -func NewTokeninfoTreeResponse(t tree.MytokenEntryTree) TokeninfoTreeResponse { - return TokeninfoTreeResponse{Tokens: t} +func NewTokeninfoTreeResponse(t tree.MytokenEntryTree, update *my.MytokenResponse) TokeninfoTreeResponse { + return TokeninfoTreeResponse{Tokens: t, TokenUpdate: update} } diff --git a/internal/endpoints/tokeninfo/tokeninfo.go b/internal/endpoints/tokeninfo/tokeninfo.go index 516484c6706698f12cb19130054ca6e02d5b835b..d736f12314de324c892e38c55d3c81f64e243b51 100644 --- a/internal/endpoints/tokeninfo/tokeninfo.go +++ b/internal/endpoints/tokeninfo/tokeninfo.go @@ -5,15 +5,19 @@ import ( "fmt" "github.com/gofiber/fiber/v2" + "github.com/oidc-mytoken/api/v0" log "github.com/sirupsen/logrus" dbhelper "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/mytokenrepohelper" + response "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" "github.com/oidc-mytoken/server/internal/endpoints/tokeninfo/pkg" "github.com/oidc-mytoken/server/internal/model" + "github.com/oidc-mytoken/server/internal/utils/cookies" "github.com/oidc-mytoken/server/internal/utils/ctxUtils" "github.com/oidc-mytoken/server/internal/utils/errorfmt" model2 "github.com/oidc-mytoken/server/shared/model" mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" + "github.com/oidc-mytoken/server/shared/mytoken/restrictions" ) // HandleTokenInfo handles requests to the tokeninfo endpoint @@ -77,3 +81,37 @@ func testMytoken(ctx *fiber.Ctx, req *pkg.TokenInfoRequest) (*mytoken.Mytoken, * } return mt, nil } + +func checkTokenInfo(mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData, capability api.Capability) (*restrictions.Restriction, *model.Response) { + if !mt.Capabilities.Has(capability) { + return nil, &model.Response{ + Status: fiber.StatusForbidden, + Response: api.ErrorInsufficientCapabilities, + } + } + var usedRestriction *restrictions.Restriction + if len(mt.Restrictions) > 0 { + possibleRestrictions := mt.Restrictions.GetValidForOther(nil, clientMetadata.IP, mt.ID) + if len(possibleRestrictions) == 0 { + return nil, &model.Response{ + Status: fiber.StatusForbidden, + Response: api.ErrorUsageRestricted, + } + } + usedRestriction = &possibleRestrictions[0] + } + return usedRestriction, nil +} + +func makeTokenInfoResponse(rsp interface{}, tokenUpdate *response.MytokenResponse) model.Response { + var cake []*fiber.Cookie + if tokenUpdate != nil { + cookie := cookies.MytokenCookie(tokenUpdate.Mytoken) + cake = []*fiber.Cookie{&cookie} + } + return model.Response{ + Status: fiber.StatusOK, + Response: rsp, + Cookies: cake, + } +} diff --git a/internal/endpoints/tokeninfo/tree.go b/internal/endpoints/tokeninfo/tree.go index 5b4876202f1ddd8314a6f16f649b9c4aa3046dc4..7d5a306e638520c24ae7516c15e8975ca167163e 100644 --- a/internal/endpoints/tokeninfo/tree.go +++ b/internal/endpoints/tokeninfo/tree.go @@ -3,51 +3,26 @@ package tokeninfo import ( "database/sql" - "github.com/gofiber/fiber/v2" "github.com/jmoiron/sqlx" "github.com/oidc-mytoken/api/v0" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/oidc-mytoken/server/internal/db" "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/tree" response "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg" "github.com/oidc-mytoken/server/internal/endpoints/tokeninfo/pkg" "github.com/oidc-mytoken/server/internal/model" - "github.com/oidc-mytoken/server/internal/utils/cookies" "github.com/oidc-mytoken/server/internal/utils/errorfmt" eventService "github.com/oidc-mytoken/server/shared/mytoken/event" event "github.com/oidc-mytoken/server/shared/mytoken/event/pkg" mytoken "github.com/oidc-mytoken/server/shared/mytoken/pkg" "github.com/oidc-mytoken/server/shared/mytoken/restrictions" "github.com/oidc-mytoken/server/shared/mytoken/rotation" - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" ) -func handleTokenInfoTree(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData) model.Response { - // If we call this function it means the token is valid. - - if !mt.Capabilities.Has(api.CapabilityTokeninfoTree) { - return model.Response{ - Status: fiber.StatusForbidden, - Response: api.ErrorInsufficientCapabilities, - } - } - - var usedRestriction *restrictions.Restriction - if len(mt.Restrictions) > 0 { - possibleRestrictions := mt.Restrictions.GetValidForOther(nil, clientMetadata.IP, mt.ID) - if len(possibleRestrictions) == 0 { - return model.Response{ - Status: fiber.StatusForbidden, - Response: api.ErrorUsageRestricted, - } - } - usedRestriction = &possibleRestrictions[0] - } - - var tokenTree tree.MytokenEntryTree - var tokenUpdate *response.MytokenResponse - if err := db.Transact(func(tx *sqlx.Tx) error { - var err error +func doTokenInfoTree(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData, usedRestriction *restrictions.Restriction) (tokenTree tree.MytokenEntryTree, tokenUpdate *response.MytokenResponse, err error) { + err = db.Transact(func(tx *sqlx.Tx) error { tokenTree, err = tree.TokenSubTree(tx, mt.ID) if err != nil && !errors.Is(err, sql.ErrNoRows) { return err @@ -67,21 +42,21 @@ func handleTokenInfoTree(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMe Event: event.FromNumber(event.MTEventTokenInfoTree, ""), MTID: mt.ID, }, *clientMetadata) - }); err != nil { - log.Errorf("%s", errorfmt.Full(err)) - return *model.ErrorToInternalServerErrorResponse(err) - } + }) + return +} - rsp := pkg.NewTokeninfoTreeResponse(tokenTree) - var cake []*fiber.Cookie - if tokenUpdate != nil { - rsp.TokenUpdate = tokenUpdate - cookie := cookies.MytokenCookie(tokenUpdate.Mytoken) - cake = []*fiber.Cookie{&cookie} +func handleTokenInfoTree(req pkg.TokenInfoRequest, mt *mytoken.Mytoken, clientMetadata *api.ClientMetaData) model.Response { + // If we call this function it means the token is valid. + usedRestriction, errRes := checkTokenInfo(mt, clientMetadata, api.CapabilityTokeninfoTree) + if errRes != nil { + return *errRes } - return model.Response{ - Status: fiber.StatusOK, - Response: rsp, - Cookies: cake, + tokenTree, tokenUpdate, err := doTokenInfoTree(req, mt, clientMetadata, usedRestriction) + if err != nil { + log.Errorf("%s", errorfmt.Full(err)) + return *model.ErrorToInternalServerErrorResponse(err) } + rsp := pkg.NewTokeninfoTreeResponse(tokenTree, tokenUpdate) + return makeTokenInfoResponse(rsp, tokenUpdate) } diff --git a/shared/mytoken/mytokenHandler.go b/shared/mytoken/mytokenHandler.go index 5d020024eea5f2b8eb64e6ed37f9266871d1dd57..6ac10b6f2585710dc0f5585744addb9540a03a4b 100644 --- a/shared/mytoken/mytokenHandler.go +++ b/shared/mytoken/mytokenHandler.go @@ -1,7 +1,6 @@ package mytoken import ( - "database/sql" "encoding/json" "fmt" "strings" @@ -210,11 +209,13 @@ func handleMytokenFromMytoken(parent *mytoken.Mytoken, req *response.MytokenFrom {event.FromNumber(event.MTEventMTCreated, strings.TrimSpace(fmt.Sprintf("Created MT %s", req.Name))), parent.ID}, }, *networkData) }); err != nil { + log.Errorf("%s", errorfmt.Full(err)) return model.ErrorToInternalServerErrorResponse(err) } res, err := ste.Token.ToTokenResponse(req.ResponseType, req.MaxTokenLen, *networkData, "") if err != nil { + log.Errorf("%s", errorfmt.Full(err)) return model.ErrorToInternalServerErrorResponse(err) } var cake []*fiber.Cookie @@ -311,12 +312,11 @@ func RevokeMytoken(tx *sqlx.Tx, id mtid.MTID, jwt string, recursive bool, issuer Response: api.ErrorUnknownIssuer, } } - if err := db.RunWithinTransaction(tx, func(tx *sqlx.Tx) error { + err := db.RunWithinTransaction(tx, func(tx *sqlx.Tx) error { rtID, err := refreshtokenrepo.GetRTID(tx, id) if err != nil { - if errors.Is(err, sql.ErrNoRows) { - return nil - } + _, err = dbhelper.ParseError(err) // sets err to nil if token was not found; + // this is no error and we are done, since the token is already revoked return err } rt, _, err := refreshtokenrepo.GetRefreshToken(tx, id, jwt) @@ -338,16 +338,16 @@ func RevokeMytoken(tx *sqlx.Tx, id mtid.MTID, jwt string, recursive bool, issuer return fmt.Errorf("%s: %s", apiError.Error, apiError.ErrorDescription) } return refreshtokenrepo.DeleteRefreshToken(tx, rtID) - }); err != nil { - if strings.HasPrefix(errorfmt.Error(err), "oidc_error") { - return &model.Response{ - Status: httpStatus.StatusOIDPError, - Response: pkgModel.OIDCError(errorfmt.Error(err), ""), - } - } else { - log.Errorf("%s", errorfmt.Full(err)) - return model.ErrorToInternalServerErrorResponse(err) + }) + if err == nil { + return nil + } + if strings.HasPrefix(errorfmt.Error(err), "oidc_error") { + return &model.Response{ + Status: httpStatus.StatusOIDPError, + Response: pkgModel.OIDCError(errorfmt.Error(err), ""), } } - return nil + log.Errorf("%s", errorfmt.Full(err)) + return model.ErrorToInternalServerErrorResponse(err) }