Skip to content
Snippets Groups Projects
Verified Commit dc3b6dd6 authored by Gabriel Zachmann's avatar Gabriel Zachmann
Browse files

WIP add tokeninfo notifications

parent 8e0b10aa
No related branches found
No related tags found
No related merge requests found
......@@ -278,3 +278,9 @@ END;
DELIMITER ;
# Table Data
INSERT IGNORE INTO Events (event)
VALUES ('tokeninfo_notifications');
INSERT IGNORE INTO Events (event)
VALUES ('tokeninfo_notifications_other_token');
......@@ -2,6 +2,7 @@ package calendarrepo
import (
"github.com/jmoiron/sqlx"
"github.com/oidc-mytoken/api/v0"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
......@@ -68,7 +69,7 @@ func GetMTsInCalendar(rlog log.Ext1FieldLogger, tx *sqlx.Tx, calendarID string)
}
// Get returns a calendar entry for a user and name
func Get(rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID mtid.MTID, name string) (info CalendarInfo, err error) {
func Get(rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID any, name string) (info CalendarInfo, err error) {
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
return errors.WithStack(tx.Get(&info, `CALL Calendar_Get(?,?)`, mtID, name))
......@@ -88,12 +89,40 @@ func GetByID(rlog log.Ext1FieldLogger, tx *sqlx.Tx, id string) (info CalendarInf
}
// List returns a list of all calendar entries for a user
func List(rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID mtid.MTID) (infos []CalendarInfo, err error) {
func List(rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID mtid.MTID) (cals []api.NotificationCalendar, err error) {
var infos []CalendarInfo
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
return errors.WithStack(tx.Select(&infos, `CALL Calendar_List(?)`, mtID))
},
)
for _, i := range infos {
cals = append(
cals, api.NotificationCalendar{
Name: i.Name,
ICSPath: i.ICSPath,
},
)
}
return
}
// ListCalendarsForMT returns a list of calendars where the passed token is used in
func ListCalendarsForMT(rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID any) (cals []api.NotificationCalendar, err error) {
var infos []CalendarInfo
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
return errors.WithStack(tx.Select(&infos, `CALL Calendar_ListForMT(?)`, mtID))
},
)
for _, i := range infos {
cals = append(
cals, api.NotificationCalendar{
Name: i.Name,
ICSPath: i.ICSPath,
},
)
}
return
}
......
......@@ -7,6 +7,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/oidc-mytoken/server/internal/db"
"github.com/oidc-mytoken/server/internal/db/notificationsrepo/calendarrepo"
"github.com/oidc-mytoken/server/internal/endpoints/notification/pkg"
"github.com/oidc-mytoken/server/internal/mytoken/pkg/mtid"
)
......@@ -62,7 +63,7 @@ func GetNotificationsForMTAndClass(
// GetNotificationsForMT checks for and returns the found notifications for a certain mytoken
func GetNotificationsForMT(
rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID mtid.MTID,
rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID any,
) (notifications []notificationInfoBaseWithClass, err error) {
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
......@@ -73,20 +74,39 @@ func GetNotificationsForMT(
return
}
// GetNotificationsForUser returns all found notifications for a user
func GetNotificationsForUser(
rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID mtid.MTID,
) (notifications []api.NotificationInfo, err error) {
func GetNotificationsAndCalendarsForMT(
rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID any,
) (notifications []api.NotificationInfo, calendars []api.NotificationCalendar, err error) {
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
var withClass []notificationInfoBaseWithClass
_, err = db.ParseError(tx.Select(&withClass, `CALL Notifications_GetForUser(?)`, mtID))
calendars, err = calendarrepo.ListCalendarsForMT(rlog, tx, mtID)
if err != nil {
return errors.WithStack(err)
return err
}
notificationMap := make(map[uint64]api.NotificationInfo)
var ids []uint64
for _, n := range withClass {
ns, err := GetNotificationsForMT(rlog, tx, mtID)
if err != nil {
return err
}
notifications, err = notificationInfoBaseWithClassToNotificationInfo(rlog, tx, ns)
return err
},
)
return
}
func notificationInfoBaseWithClassToNotificationInfo(
rlog log.Ext1FieldLogger, tx *sqlx.Tx,
in []notificationInfoBaseWithClass,
) (
out []api.
NotificationInfo,
err error,
) {
notificationMap := make(map[uint64]api.NotificationInfo)
var ids []uint64
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
for _, n := range in {
nie, ok := notificationMap[n.NotificationID]
if ok {
nie.Classes = append(nie.Classes, api.NewNotificationClass(n.Class))
......@@ -108,12 +128,33 @@ func GetNotificationsForUser(
}
notificationMap[nie.NotificationID] = nie
}
for _, id := range ids {
notifications = append(notifications, notificationMap[id])
}
return nil
},
)
if err != nil {
return
}
for _, id := range ids {
out = append(out, notificationMap[id])
}
return
}
// GetNotificationsForUser returns all found notifications for a user
func GetNotificationsForUser(
rlog log.Ext1FieldLogger, tx *sqlx.Tx, mtID mtid.MTID,
) (notifications []api.NotificationInfo, err error) {
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
var withClass []notificationInfoBaseWithClass
_, err = db.ParseError(tx.Select(&withClass, `CALL Notifications_GetForUser(?)`, mtID))
if err != nil {
return errors.WithStack(err)
}
notifications, err = notificationInfoBaseWithClassToNotificationInfo(rlog, tx, withClass)
return err
},
)
return
}
......
......@@ -123,6 +123,7 @@ func HandleAdd(ctx *fiber.Ctx) error {
)
icsPath := utils.CombineURLPath(routes.CalendarDownloadEndpoint, id)
cal.SetUrl(icsPath)
calendarInfo.ICSPath = icsPath
dbInfo := calendarrepo.CalendarInfo{
ID: id,
Name: calendarInfo.Name,
......@@ -131,7 +132,7 @@ func HandleAdd(ctx *fiber.Ctx) error {
}
res := model.Response{
Status: http.StatusCreated,
Response: pkg.CreateCalendarResponse{CalendarInfo: dbInfo},
Response: pkg.CreateCalendarResponse{NotificationCalendar: calendarInfo},
}
if err := db.Transact(
rlog, func(tx *sqlx.Tx) error {
......
......@@ -3,7 +3,6 @@ package pkg
import (
"github.com/oidc-mytoken/api/v0"
"github.com/oidc-mytoken/server/internal/db/notificationsrepo/calendarrepo"
"github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg"
"github.com/oidc-mytoken/server/internal/mytoken/pkg/mtid"
)
......@@ -16,12 +15,12 @@ type AddMytokenToCalendarRequest struct {
// CreateCalendarResponse is the response returned when a new calendar is created
type CreateCalendarResponse struct {
calendarrepo.CalendarInfo
api.NotificationCalendar
TokenUpdate *pkg.MytokenResponse `json:"token_update,omitempty"`
}
// CalendarListResponse is the response returned to list all calendars of a user
type CalendarListResponse struct {
Calendars []calendarrepo.CalendarInfo `json:"calendars"`
TokenUpdate *pkg.MytokenResponse `json:"token_update,omitempty"`
Calendars []api.NotificationCalendar `json:"calendars"`
TokenUpdate *pkg.MytokenResponse `json:"token_update,omitempty"`
}
......@@ -125,8 +125,8 @@ func HandleTokenInfoHistory(
}
return handleTokenInfoHistory(rlog, tx, req, mt, clientMetadata)
}
if !mt.Capabilities.Has(api.CapabilityHistoryAnyToken) {
for _, momid := range req.MOMIDs {
for _, momid := range req.MOMIDs {
if !mt.Capabilities.Has(api.CapabilityHistoryAnyToken) {
if momid == api.MOMIDValueThis || momid == api.MOMIDValueChildren {
continue
}
......@@ -150,21 +150,21 @@ func HandleTokenInfoHistory(
},
}
}
}
same, err := helper.CheckMytokensAreForSameUser(rlog, tx, momid, mt.ID)
if err != nil {
return *model.ErrorToInternalServerErrorResponse(err)
}
if !same {
return model.Response{
Status: fiber.StatusForbidden,
Response: api.Error{
Error: api.ErrorStrInvalidGrant,
ErrorDescription: fmt.Sprintf(
"The provided token cannot be used to obtain history for mom_id '%s'", momid,
),
},
}
same, err := helper.CheckMytokensAreForSameUser(rlog, tx, momid, mt.ID)
if err != nil {
return *model.ErrorToInternalServerErrorResponse(err)
}
if !same {
return model.Response{
Status: fiber.StatusForbidden,
Response: api.Error{
Error: api.ErrorStrInvalidGrant,
ErrorDescription: fmt.Sprintf(
"The provided token cannot be used to obtain history for mom_id '%s'", momid,
),
},
}
}
}
......
package tokeninfo
import (
"fmt"
"github.com/gofiber/fiber/v2"
"github.com/jmoiron/sqlx"
"github.com/oidc-mytoken/api/v0"
log "github.com/sirupsen/logrus"
"github.com/oidc-mytoken/server/internal/db"
helper "github.com/oidc-mytoken/server/internal/db/dbrepo/mytokenrepo/mytokenrepohelper"
"github.com/oidc-mytoken/server/internal/db/notificationsrepo"
"github.com/oidc-mytoken/server/internal/endpoints/tokeninfo/pkg"
"github.com/oidc-mytoken/server/internal/model"
eventService "github.com/oidc-mytoken/server/internal/mytoken/event"
pkg2 "github.com/oidc-mytoken/server/internal/mytoken/event/pkg"
mytoken "github.com/oidc-mytoken/server/internal/mytoken/pkg"
"github.com/oidc-mytoken/server/internal/mytoken/restrictions"
"github.com/oidc-mytoken/server/internal/mytoken/rotation"
"github.com/oidc-mytoken/server/internal/utils/auth"
"github.com/oidc-mytoken/server/internal/utils/cookies"
"github.com/oidc-mytoken/server/internal/utils/errorfmt"
)
func doTokenInfoNotifications(
rlog log.Ext1FieldLogger, tx *sqlx.Tx, req *pkg.TokenInfoRequest, mt *mytoken.Mytoken,
clientMetadata *api.ClientMetaData,
usedRestriction *restrictions.Restriction,
) (res pkg.TokeninfoNotificationsResponse, err error) {
err = db.RunWithinTransaction(
rlog, tx, func(tx *sqlx.Tx) error {
if len(req.MOMIDs) > 1 {
res.MomIDMapping = make(map[string]api.NotificationsCombinedResponse)
for _, id := range req.MOMIDs {
if id == api.MOMIDValueThis {
id = mt.ID.String()
}
var data api.NotificationsCombinedResponse
data.Notifications, data.Calendars, err = notificationsrepo.GetNotificationsAndCalendarsForMT(
rlog, tx, id,
)
if err != nil {
return err
}
res.MomIDMapping[id] = data
}
} else {
var id any
id = mt.ID
if len(req.MOMIDs) > 0 {
id = req.MOMIDs[0]
}
res.Notifications, res.Calendars, err = notificationsrepo.GetNotificationsAndCalendarsForMT(
rlog, tx, id,
)
if err != nil {
return err
}
}
if usedRestriction == nil {
return nil
}
if err = usedRestriction.UsedOther(rlog, tx, mt.ID); err != nil {
return err
}
res.TokenUpdate, err = rotation.RotateMytokenAfterOtherForResponse(
rlog, tx, req.Mytoken.JWT, mt, *clientMetadata, req.Mytoken.OriginalTokenType,
)
if err != nil {
return err
}
ev := api.EventTokenInfoNotifications
if len(req.MOMIDs) > 0 {
ev = api.EventTokenInfoNotificationsOtherToken
}
return eventService.LogEvent(
rlog, tx, pkg2.MTEvent{
Event: ev,
MTID: mt.ID,
ClientMetaData: *clientMetadata,
},
)
},
)
return
}
func handleTokenInfoNotifications(
rlog log.Ext1FieldLogger, tx *sqlx.Tx, req *pkg.TokenInfoRequest, mt *mytoken.Mytoken,
clientMetadata *api.ClientMetaData,
) model.Response {
usedRestriction, errRes := auth.RequireUsableRestrictionOther(rlog, nil, mt, clientMetadata)
if errRes != nil {
return *errRes
}
res, err := doTokenInfoNotifications(rlog, tx, req, mt, clientMetadata, usedRestriction)
if err != nil {
rlog.Errorf("%s", errorfmt.Full(err))
return *model.ErrorToInternalServerErrorResponse(err)
}
rsp := model.Response{
Status: fiber.StatusOK,
Response: res,
}
if res.TokenUpdate != nil {
rsp.Cookies = []*fiber.Cookie{cookies.MytokenCookie(res.TokenUpdate.Mytoken)}
}
return rsp
}
// HandleTokenInfoNotifications handles a tokeninfo notifications request
func HandleTokenInfoNotifications(
rlog log.Ext1FieldLogger, tx *sqlx.Tx, req *pkg.TokenInfoRequest, mt *mytoken.Mytoken,
clientMetadata *api.ClientMetaData,
) model.Response {
// If we call this function it means the token is valid.
rlog.Debug("Handle tokeninfo notifications request")
if len(req.MOMIDs) == 0 {
if errRes := auth.RequireCapability(
rlog, tx, api.CapabilityTokeninfoNotify, mt, clientMetadata,
); errRes != nil {
return *errRes
}
return handleTokenInfoNotifications(rlog, tx, req, mt, clientMetadata)
}
for _, momid := range req.MOMIDs {
if !mt.Capabilities.Has(api.CapabilityNotifyAnyTokenRead) {
if momid == api.MOMIDValueThis {
continue
}
isParent, err := helper.MOMIDHasParent(rlog, tx, momid, mt.ID)
if err != nil {
return *model.ErrorToInternalServerErrorResponse(err)
}
if !isParent {
return model.Response{
Status: fiber.StatusForbidden,
Response: api.Error{
Error: api.ErrorStrInsufficientCapabilities,
ErrorDescription: fmt.Sprintf(
"The provided token is neither a parent of the the token with "+
" mom_id '%s' nor does it have the '%s' capability", momid,
api.CapabilityNotifyAnyTokenRead.Name,
),
},
}
}
}
same, err := helper.CheckMytokensAreForSameUser(rlog, tx, momid, mt.ID)
if err != nil {
return *model.ErrorToInternalServerErrorResponse(err)
}
if !same {
return model.Response{
Status: fiber.StatusForbidden,
Response: api.Error{
Error: api.ErrorStrInvalidGrant,
ErrorDescription: fmt.Sprintf(
"The provided token cannot be used to obtain notifications for mom_id '%s'", momid,
),
},
}
}
}
return handleTokenInfoNotifications(rlog, tx, req, mt, clientMetadata)
}
package pkg
import (
"github.com/oidc-mytoken/api/v0"
my "github.com/oidc-mytoken/server/internal/endpoints/token/mytoken/pkg"
)
// TokeninfoNotificationsResponse is a type for responses to tokeninfo notification requests
type TokeninfoNotificationsResponse struct {
// on update check api.TokeninfoNotificationsResponse
api.TokeninfoNotificationsResponse
TokenUpdate *my.MytokenResponse `json:"token_update,omitempty"`
}
......@@ -30,6 +30,8 @@ func HandleTokenInfo(ctx *fiber.Ctx) error {
switch req.Action {
case model.TokeninfoActionIntrospect:
return HandleTokenInfoIntrospect(rlog, nil, mt, req.Mytoken.OriginalTokenType, clientMetadata).Send(ctx)
case model.TokeninfoActionNotifications:
return HandleTokenInfoNotifications(rlog, nil, &req, mt, clientMetadata).Send(ctx)
case model.TokeninfoActionEventHistory:
return HandleTokenInfoHistory(rlog, nil, &req, mt, clientMetadata).Send(ctx)
case model.TokeninfoActionSubtokenTree:
......
......@@ -20,6 +20,7 @@ const ( // assert that these are in the same order as api.AllTokeninfoActions
TokeninfoActionEventHistory
TokeninfoActionSubtokenTree
TokeninfoActionListMytokens
TokeninfoActionNotifications
maxTokeninfoAction
)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment