Livio Amstutz 2ba56595b1
feat: add apis and application keys (#1327)
* feat: add apis and application keys

* VerifyOIDCClientSecret

* Update internal/v2/repository/project/api_config.go

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>

* Update internal/v2/repository/project/key.go

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>

* fix append ApplicationKeyWriteModel

Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
2021-02-22 12:27:47 +01:00

207 lines
6.4 KiB
Go

package eventsourcing
import (
"context"
"encoding/json"
"strings"
"github.com/caos/logging"
"github.com/golang/protobuf/ptypes"
"github.com/caos/zitadel/internal/cache/config"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore"
es_models "github.com/caos/zitadel/internal/eventstore/models"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
"github.com/caos/zitadel/internal/id"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
)
const (
projectOwnerRole = "PROJECT_OWNER"
projectOwnerGlobalRole = "PROJECT_OWNER_GLOBAL"
)
type ProjectEventstore struct {
es_int.Eventstore
projectCache *ProjectCache
passwordAlg crypto.HashAlgorithm
pwGenerator crypto.Generator
idGenerator id.Generator
ClientKeySize int
}
type ProjectConfig struct {
es_int.Eventstore
Cache *config.CacheConfig
}
func StartProject(conf ProjectConfig, systemDefaults sd.SystemDefaults) (*ProjectEventstore, error) {
projectCache, err := StartCache(conf.Cache)
if err != nil {
return nil, err
}
passwordAlg := crypto.NewBCrypt(systemDefaults.SecretGenerators.PasswordSaltCost)
pwGenerator := crypto.NewHashGenerator(systemDefaults.SecretGenerators.ClientSecretGenerator, passwordAlg)
return &ProjectEventstore{
Eventstore: conf.Eventstore,
projectCache: projectCache,
passwordAlg: passwordAlg,
pwGenerator: pwGenerator,
idGenerator: id.SonyFlakeGenerator,
ClientKeySize: int(systemDefaults.SecretGenerators.ApplicationKeySize),
}, nil
}
func (es *ProjectEventstore) ProjectByID(ctx context.Context, id string) (*proj_model.Project, error) {
project := es.projectCache.getProject(id)
query, err := ProjectByIDQuery(project.AggregateID, project.Sequence)
if err != nil {
return nil, err
}
err = es_sdk.Filter(ctx, es.FilterEvents, project.AppendEvents, query)
if err != nil && !(caos_errs.IsNotFound(err) && project.Sequence != 0) {
return nil, err
}
if project.State == int32(proj_model.ProjectStateRemoved) {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-dG8ie", "Errors.Project.NotFound")
}
es.projectCache.cacheProject(project)
return model.ProjectToModel(project), nil
}
func (es *ProjectEventstore) ProjectEventsByID(ctx context.Context, id string, sequence uint64) ([]*es_models.Event, error) {
query, err := ProjectByIDQuery(id, sequence)
if err != nil {
return nil, err
}
return es.FilterEvents(ctx, query)
}
func (es *ProjectEventstore) ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*proj_model.ProjectChanges, error) {
query := ChangesQuery(id, lastSequence, limit, sortAscending)
events, err := es.Eventstore.FilterEvents(context.Background(), query)
if err != nil {
logging.Log("EVENT-ZRffs").WithError(err).Warn("eventstore unavailable")
return nil, errors.ThrowInternal(err, "EVENT-328b1", "Errors.Internal")
}
if len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-FpQqK", "Errors.Changes.NotFound")
}
changes := make([]*proj_model.ProjectChange, len(events))
for i, event := range events {
creationDate, err := ptypes.TimestampProto(event.CreationDate)
logging.Log("EVENT-qxIR7").OnError(err).Debug("unable to parse timestamp")
change := &proj_model.ProjectChange{
ChangeDate: creationDate,
EventType: event.Type.String(),
ModifierId: event.EditorUser,
Sequence: event.Sequence,
}
if event.Data != nil {
var data interface{}
if strings.Contains(change.EventType, "application") {
data = new(model.Application)
} else {
data = new(model.Project)
}
err = json.Unmarshal(event.Data, data)
logging.Log("EVENT-NCkpN").OnError(err).Debug("unable to unmarshal data")
change.Data = data
}
changes[i] = change
if lastSequence < event.Sequence {
lastSequence = event.Sequence
}
}
return &proj_model.ProjectChanges{
Changes: changes,
LastSequence: lastSequence,
}, nil
}
func ChangesQuery(projectID string, latestSequence, limit uint64, sortAscending bool) *es_models.SearchQuery {
query := es_models.NewSearchQuery().
AggregateTypeFilter(model.ProjectAggregate)
if !sortAscending {
query.OrderDesc()
}
query.LatestSequenceFilter(latestSequence).
AggregateIDFilter(projectID).
SetLimit(limit)
return query
}
func (es *ProjectEventstore) ApplicationByIDs(ctx context.Context, projectID, appID string) (*proj_model.Application, error) {
if projectID == "" || appID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-ld93d", "Errors.Project.IDMissing")
}
project, err := es.ProjectByID(ctx, projectID)
if err != nil {
return nil, err
}
if _, a := project.GetApp(appID); a != nil {
return a, nil
}
return nil, caos_errs.ThrowNotFound(nil, "EVENT-8ei2s", "Errors.Project.App.NotFound")
}
func (es *ProjectEventstore) ApplicationChanges(ctx context.Context, projectID string, appID string, lastSequence uint64, limit uint64, sortAscending bool) (*proj_model.ApplicationChanges, error) {
query := ChangesQuery(projectID, lastSequence, limit, sortAscending)
events, err := es.Eventstore.FilterEvents(ctx, query)
if err != nil {
logging.Log("EVENT-ZRffs").WithError(err).Warn("eventstore unavailable")
return nil, errors.ThrowInternal(err, "EVENT-sw6Ku", "Errors.Internal")
}
if len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-9IHLP", "Errors.Changes.NotFound")
}
result := make([]*proj_model.ApplicationChange, 0)
for _, event := range events {
if !strings.Contains(event.Type.String(), "application") || event.Data == nil {
continue
}
app := new(model.Application)
err := json.Unmarshal(event.Data, app)
logging.Log("EVENT-GIiKD").OnError(err).Debug("unable to unmarshal data")
if app.AppID != appID {
continue
}
creationDate, err := ptypes.TimestampProto(event.CreationDate)
logging.Log("EVENT-MJzeN").OnError(err).Debug("unable to parse timestamp")
result = append(result, &proj_model.ApplicationChange{
ChangeDate: creationDate,
EventType: event.Type.String(),
ModifierId: event.EditorUser,
Sequence: event.Sequence,
Data: app,
})
if lastSequence < event.Sequence {
lastSequence = event.Sequence
}
}
return &proj_model.ApplicationChanges{
Changes: result,
LastSequence: lastSequence,
}, nil
}