Merge pull request #461 from openziti/add.api.session.timeout.response

adds expiration-seconds and expires-at headers
This commit is contained in:
Andrew
2021-01-15 09:59:50 -05:00
committed by GitHub
10 changed files with 76 additions and 13 deletions
@@ -126,28 +126,30 @@ func (ro *AuthRouter) authHandler(ae *env.AppEnv, rc *response.RequestContext, p
ConfigTypes: configTypes,
IPAddress: remoteIpStr,
}
sessionId, err := ae.Handlers.ApiSession.Create(s)
apiSessionId, err := ae.Handlers.ApiSession.Create(s)
if err != nil {
rc.RespondWithError(err)
return
}
session, err := ae.Handlers.ApiSession.Read(sessionId)
updatedApiSesion, err := ae.Handlers.ApiSession.Read(apiSessionId)
if err != nil {
logger.WithField("cause", err).Error("loading session by id resulted in an error")
rc.RespondWithApiError(apierror.NewUnauthorized())
}
apiSession := MapToCurrentApiSessionRestModel(session, ae.Config.SessionTimeoutDuration())
rc.ApiSession = updatedApiSesion
apiSession := MapToCurrentApiSessionRestModel(updatedApiSesion, ae.Config.SessionTimeoutDuration())
envelope := &rest_model.CurrentAPISessionDetailEnvelope{Data: apiSession, Meta: &rest_model.Meta{}}
expiration := time.Time(*apiSession.ExpiresAt)
cookie := http.Cookie{Name: ae.AuthCookieName, Value: token, Expires: expiration}
rc.ResponseWriter.Header().Set(ae.AuthHeaderName, session.Token)
rc.ResponseWriter.Header().Set(ae.AuthHeaderName, updatedApiSesion.Token)
http.SetCookie(rc.ResponseWriter, &cookie)
ro.createTimer.UpdateSince(start)
+1
View File
@@ -285,6 +285,7 @@ func (r *CaRouter) generateJwt(ae *env.AppEnv, rc *response.RequestContext) {
rc.ResponseWriter.Header().Set("content-type", "application/jwt")
response.AddVersionHeader(rc.ResponseWriter)
response.AddSessionExpirationHeader(rc)
rc.ResponseWriter.WriteHeader(http.StatusOK)
_, _ = rc.ResponseWriter.Write([]byte(jwt))
}
@@ -59,6 +59,7 @@ func (factory *CurrentApiSessionLinkFactoryImpl) Links(entity models.Entity) res
func MapToCurrentApiSessionRestModel(s *model.ApiSession, sessionTimeout time.Duration) *rest_model.CurrentAPISessionDetail {
expiresAt := strfmt.DateTime(s.UpdatedAt.Add(sessionTimeout))
expirationSeconds := int64(s.ExpirationDuration.Seconds())
apiSession := &rest_model.CurrentAPISessionDetail{
APISessionDetail: rest_model.APISessionDetail{
BaseEntity: BaseEntityToRestModel(s, CurrentApiSessionLinkFactory),
@@ -67,7 +68,8 @@ func MapToCurrentApiSessionRestModel(s *model.ApiSession, sessionTimeout time.Du
ConfigTypes: stringz.SetToSlice(s.ConfigTypes),
IPAddress: &s.IPAddress,
},
ExpiresAt: &expiresAt,
ExpiresAt: &expiresAt,
ExpirationSeconds: &expirationSeconds,
}
return apiSession
+11 -5
View File
@@ -25,15 +25,18 @@ import (
"github.com/pkg/errors"
"go.etcd.io/bbolt"
"reflect"
"time"
)
type ApiSession struct {
models.BaseEntity
Token string
IdentityId string
Identity *Identity
IPAddress string
ConfigTypes map[string]struct{}
Token string
IdentityId string
Identity *Identity
IPAddress string
ConfigTypes map[string]struct{}
ExpiresAt time.Time
ExpirationDuration time.Duration
}
func (entity *ApiSession) toBoltEntity(tx *bbolt.Tx, handler Handler) (boltz.Entity, error) {
@@ -74,6 +77,9 @@ func (entity *ApiSession) fillFrom(handler Handler, tx *bbolt.Tx, boltEntity bol
entity.IdentityId = boltApiSession.IdentityId
entity.ConfigTypes = stringz.SliceToSet(boltApiSession.ConfigTypes)
entity.IPAddress = boltApiSession.IPAddress
entity.ExpiresAt = entity.UpdatedAt.Add(handler.GetEnv().GetConfig().Api.SessionTimeoutSeconds)
entity.ExpirationDuration = handler.GetEnv().GetConfig().Api.SessionTimeoutSeconds
boltIdentity, err := handler.GetEnv().GetStores().Identity.LoadOneById(tx, boltApiSession.IdentityId)
if err != nil {
return err
+13
View File
@@ -21,9 +21,22 @@ import (
"github.com/openziti/edge/build"
version3 "github.com/openziti/edge/internal/version"
"net/http"
"strconv"
)
const (
ApiSessionExpirationSecondsHeader = "expiration-seconds"
ApiSessionExpiresAtheader = "expires-at"
)
func AddVersionHeader(rw http.ResponseWriter) {
buildInfo := build.GetBuildInfo()
rw.Header().Set(ZitiControllerVersionHeader, fmt.Sprintf("%s/%s/%s", buildInfo.Version(), buildInfo.Revision(), version3.GetApiVersion()))
}
func AddSessionExpirationHeader(rc *RequestContext) {
if rc.ApiSession != nil {
rc.ResponseWriter.Header().Set(ApiSessionExpirationSecondsHeader, strconv.FormatInt(int64(rc.ApiSession.ExpirationDuration.Seconds()), 10))
rc.ResponseWriter.Header().Set(ApiSessionExpiresAtheader, rc.ApiSession.ExpiresAt.String())
}
}
+3
View File
@@ -115,14 +115,17 @@ func (responder *ResponderImpl) RespondWithOk(data interface{}, meta *rest_model
}
func (responder *ResponderImpl) Respond(data interface{}, httpStatus int) {
AddSessionExpirationHeader(responder.rc)
Respond(responder.rc.ResponseWriter, responder.rc.Request.URL.Path, responder.rc.Id, responder.GetProducer(), data, httpStatus)
}
func (responder *ResponderImpl) RespondWithError(err error) {
AddSessionExpirationHeader(responder.rc)
RespondWithError(responder.rc.ResponseWriter, responder.rc.Request, responder.rc.Id, responder.GetProducer(), err)
}
func (responder *ResponderImpl) RespondWithApiError(apiError *apierror.ApiError) {
AddSessionExpirationHeader(responder.rc)
RespondWithApiError(responder.rc.ResponseWriter, responder.rc.Request, responder.rc.Id, responder.GetProducer(), apiError)
}
+25
View File
@@ -42,6 +42,10 @@ import (
type CurrentAPISessionDetail struct {
APISessionDetail
// expiration seconds
// Required: true
ExpirationSeconds *int64 `json:"expirationSeconds"`
// expires at
// Required: true
// Format: date-time
@@ -59,12 +63,16 @@ func (m *CurrentAPISessionDetail) UnmarshalJSON(raw []byte) error {
// AO1
var dataAO1 struct {
ExpirationSeconds *int64 `json:"expirationSeconds"`
ExpiresAt *strfmt.DateTime `json:"expiresAt"`
}
if err := swag.ReadJSON(raw, &dataAO1); err != nil {
return err
}
m.ExpirationSeconds = dataAO1.ExpirationSeconds
m.ExpiresAt = dataAO1.ExpiresAt
return nil
@@ -80,9 +88,13 @@ func (m CurrentAPISessionDetail) MarshalJSON() ([]byte, error) {
}
_parts = append(_parts, aO0)
var dataAO1 struct {
ExpirationSeconds *int64 `json:"expirationSeconds"`
ExpiresAt *strfmt.DateTime `json:"expiresAt"`
}
dataAO1.ExpirationSeconds = m.ExpirationSeconds
dataAO1.ExpiresAt = m.ExpiresAt
jsonDataAO1, errAO1 := swag.WriteJSON(dataAO1)
@@ -102,6 +114,10 @@ func (m *CurrentAPISessionDetail) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateExpirationSeconds(formats); err != nil {
res = append(res, err)
}
if err := m.validateExpiresAt(formats); err != nil {
res = append(res, err)
}
@@ -112,6 +128,15 @@ func (m *CurrentAPISessionDetail) Validate(formats strfmt.Registry) error {
return nil
}
func (m *CurrentAPISessionDetail) validateExpirationSeconds(formats strfmt.Registry) error {
if err := validate.Required("expirationSeconds", "body", m.ExpirationSeconds); err != nil {
return err
}
return nil
}
func (m *CurrentAPISessionDetail) validateExpiresAt(formats strfmt.Registry) error {
if err := validate.Required("expiresAt", "body", m.ExpiresAt); err != nil {
+1 -1
View File
@@ -99,4 +99,4 @@ func unmarshalPostureCheckList(data []byte, consumer runtime.Consumer) (PostureC
switch getType.Empty {
}
return nil, errors.New(422, "invalid value: %q", getType.Empty)
}
}
+10 -2
View File
@@ -6583,9 +6583,13 @@ func init() {
{
"type": "object",
"required": [
"expiresAt"
"expiresAt",
"expirationSeconds"
],
"properties": {
"expirationSeconds": {
"type": "integer"
},
"expiresAt": {
"type": "string",
"format": "date-time"
@@ -24882,9 +24886,13 @@ func init() {
{
"type": "object",
"required": [
"expiresAt"
"expiresAt",
"expirationSeconds"
],
"properties": {
"expirationSeconds": {
"type": "integer"
},
"expiresAt": {
"type": "string",
"format": "date-time"
+3
View File
@@ -4136,10 +4136,13 @@ definitions:
- type: object
required:
- expiresAt
- expirationSeconds
properties:
expiresAt:
type: string
format: date-time
expirationSeconds:
type: integer
###################################################################
# Identities
##################################################################