feat: Login, OP Support and Auth Queries (#177)

* fix: change oidc config

* fix: change oidc config secret

* begin models

* begin repo

* fix: implement grpc app funcs

* fix: add application requests

* fix: converter

* fix: converter

* fix: converter and generate clientid

* fix: tests

* feat: project grant aggregate

* feat: project grant

* fix: project grant check if role existing

* fix: project grant requests

* fix: project grant fixes

* fix: project grant member model

* fix: project grant member aggregate

* fix: project grant member eventstore

* fix: project grant member requests

* feat: user model

* begin repo

* repo models and more

* feat: user command side

* lots of functions

* user command side

* profile requests

* commit before rebase on user

* save

* local config with gopass and more

* begin new auth command (user centric)

* Update internal/user/model/user.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/address.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/address.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/email.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/email.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/email.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/mfa.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/mfa.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/password.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/password.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/password.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/phone.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/phone.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/phone.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/user.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/user.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/model/user.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/usergrant/repository/eventsourcing/model/user_grant.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/usergrant/repository/eventsourcing/model/user_grant.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/usergrant/repository/eventsourcing/user_grant.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/user_test.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* Update internal/user/repository/eventsourcing/eventstore_mock_test.go

Co-Authored-By: Livio Amstutz <livio.a@gmail.com>

* changes from mr review

* save files into basedir

* changes from mr review

* changes from mr review

* move to auth request

* Update internal/usergrant/repository/eventsourcing/cache.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/usergrant/repository/eventsourcing/cache.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* changes requested on mr

* fix generate codes

* fix return if no events

* password code

* email verification step

* more steps

* lot of mfa

* begin tests

* more next steps

* auth api

* auth api (user)

* auth api (user)

* auth api (user)

* differ requests

* merge

* tests

* fix compilation error

* mock for id generator

* Update internal/user/repository/eventsourcing/model/password.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* Update internal/user/repository/eventsourcing/model/user.go

Co-authored-by: Silvan <silvan.reusser@gmail.com>

* requests of mr

* check email

* begin separation of command and query

* otp

* change packages

* some cleanup and fixes

* tests for auth request / next steps

* add VerificationLifetimes to config and make it run

* tests

* fix code challenge validation

* cleanup

* fix merge

* begin view

* repackaging tests and configs

* fix startup config for auth

* add migration

* add PromptSelectAccount

* fix copy / paste

* remove user_agent files

* fixes

* fix sequences in user_session

* token commands

* token queries and signout

* fix

* fix set password test

* add token handler and table

* handle session init

* add session state

* add user view test cases

* change VerifyMyMfaOTP

* some fixes

* fix user repo in auth api

* cleanup

* add user session view test

* fix merge

* begin oidc

* user agent and more

* config

* keys

* key command and query

* add login statics

* key handler

* start login

* login handlers

* lot of fixes

* merge oidc

* add missing exports

* add missing exports

* fix some bugs

* authrequestid in htmls

* getrequest

* update auth request

* fix userid check

* add username to authrequest

* fix user session and auth request handling

* fix UserSessionsByAgentID

* fix auth request tests

* fix user session on UserPasswordChanged and MfaOtpRemoved

* fix MfaTypesSetupPossible

* handle mfa

* fill username

* auth request query checks new events

* fix userSessionByIDs

* fix tokens

* fix userSessionByIDs test

* add user selection

* init code

* user code creation date

* add init user step

* add verification failed types

* add verification failures

* verify init code

* user init code handle

* user init code handle

* fix userSessionByIDs

* update logging

* user agent cookie

* browserinfo from request

* add DeleteAuthRequest

* add static login files to binary

* add login statik to build

* move generate to separate file and remove statik.go files

* remove static dirs from startup.yaml

* generate into separate namespaces

* merge master

* auth request code

* auth request type mapping

* fix keys

* improve tokens

* improve register and basic styling

* fix ailerons font

* improve password reset

* add audience to token

* all oidc apps as audience

* fix test nextStep

* fix email texts

* remove "not set"

* lot of style changes

* improve copy to clipboard

* fix footer

* add cookie handler

* remove placeholders

* fix compilation after merge

* fix auth config

* remove comments

* typo

* use new secrets store

* change default pws to match default policy

* fixes

* add todo

* enable login

* fix db name

* Auth queries (#179)

* my usersession

* org structure/ auth handlers

* working user grant spooler

* auth internal user grants

* search my project orgs

* remove permissions file

* my zitadel permissions

* my zitadel permissions

* remove unused code

* authz

* app searches in view

* token verification

* fix user grant load

* fix tests

* fix tests

* read configs

* remove unused const

* remove todos

* env variables

* app_name

* working authz

* search projects

* global resourceowner

* Update internal/api/auth/permissions.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* Update internal/api/auth/permissions.go

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* model2 rename

* at least it works

* check token expiry

* search my user grants

* remove token table from authz

Co-authored-by: Livio Amstutz <livio.a@gmail.com>

* fix test

* fix ports and enable console

Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
Livio Amstutz
2020-06-05 07:50:04 +02:00
committed by GitHub
parent 46b60a6968
commit 8a5badddf6
293 changed files with 14189 additions and 3176 deletions

View File

@@ -0,0 +1,96 @@
package model
import (
"encoding/json"
"reflect"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/org/model"
)
type OrgMember struct {
es_models.ObjectRoot `json:"-"`
UserID string `json:"userId,omitempty"`
Roles []string `json:"roles,omitempty"`
}
func (m *OrgMember) AppendEvents(events ...*es_models.Event) error {
for _, event := range events {
err := m.AppendEvent(event)
if err != nil {
return err
}
}
return nil
}
func (m *OrgMember) AppendEvent(event *es_models.Event) error {
m.ObjectRoot.AppendEvent(event)
return m.SetData(event)
}
func (m *OrgMember) SetData(event *es_models.Event) error {
err := json.Unmarshal(event.Data, m)
if err != nil {
return errors.ThrowInternal(err, "EVENT-Hz7Mb", "unable to unmarshal data")
}
return nil
}
func (m *OrgMember) Changes(updatedMember *OrgMember) map[string]interface{} {
changes := make(map[string]interface{}, 2)
if !reflect.DeepEqual(m.Roles, updatedMember.Roles) {
changes["roles"] = updatedMember.Roles
changes["userId"] = m.UserID
}
return changes
}
func OrgMemberFromEvent(member *OrgMember, event *es_models.Event) (*OrgMember, error) {
if member == nil {
member = new(OrgMember)
}
member.ObjectRoot.AppendEvent(event)
err := json.Unmarshal(event.Data, member)
if err != nil {
return nil, errors.ThrowInternal(err, "EVENT-D4qxo", "invalid event data")
}
return member, nil
}
func OrgMembersFromModel(members []*model.OrgMember) []*OrgMember {
convertedMembers := make([]*OrgMember, len(members))
for i, m := range members {
convertedMembers[i] = OrgMemberFromModel(m)
}
return convertedMembers
}
func OrgMemberFromModel(member *model.OrgMember) *OrgMember {
return &OrgMember{
ObjectRoot: member.ObjectRoot,
UserID: member.UserID,
Roles: member.Roles,
}
}
func OrgMembersToModel(members []*OrgMember) []*model.OrgMember {
convertedMembers := make([]*model.OrgMember, len(members))
for i, m := range members {
convertedMembers[i] = OrgMemberToModel(m)
}
return convertedMembers
}
func OrgMemberToModel(member *OrgMember) *model.OrgMember {
return &model.OrgMember{
ObjectRoot: member.ObjectRoot,
UserID: member.UserID,
Roles: member.Roles,
}
}

View File

@@ -0,0 +1,159 @@
package model
import (
"encoding/json"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
org_model "github.com/caos/zitadel/internal/org/model"
)
const (
OrgVersion = "v1"
)
type Org struct {
es_models.ObjectRoot `json:"-"`
Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"`
State int32 `json:"-"`
Members []*OrgMember `json:"-"`
}
func OrgFromModel(org *org_model.Org) *Org {
members := OrgMembersFromModel(org.Members)
return &Org{
ObjectRoot: org.ObjectRoot,
Domain: org.Domain,
Name: org.Name,
State: int32(org.State),
Members: members,
}
}
func OrgToModel(org *Org) *org_model.Org {
return &org_model.Org{
ObjectRoot: org.ObjectRoot,
Domain: org.Domain,
Name: org.Name,
State: org_model.OrgState(org.State),
Members: OrgMembersToModel(org.Members),
}
}
func OrgFromEvents(org *Org, events ...*es_models.Event) (*Org, error) {
if org == nil {
org = new(Org)
}
return org, org.AppendEvents(events...)
}
func (o *Org) AppendEvents(events ...*es_models.Event) error {
for _, event := range events {
err := o.AppendEvent(event)
if err != nil {
return err
}
}
return nil
}
func (o *Org) AppendEvent(event *es_models.Event) error {
switch event.Type {
case OrgAdded:
*o = Org{}
err := o.setData(event)
if err != nil {
return err
}
case OrgChanged:
err := o.setData(event)
if err != nil {
return err
}
case OrgDeactivated:
o.State = int32(org_model.ORGSTATE_INACTIVE)
case OrgReactivated:
o.State = int32(org_model.ORGSTATE_ACTIVE)
case OrgMemberAdded:
member, err := OrgMemberFromEvent(nil, event)
if err != nil {
return err
}
member.CreationDate = event.CreationDate
o.setMember(member)
case OrgMemberChanged:
member, err := OrgMemberFromEvent(nil, event)
if err != nil {
return err
}
existingMember := o.getMember(member.UserID)
member.CreationDate = existingMember.CreationDate
o.setMember(member)
case OrgMemberRemoved:
member, err := OrgMemberFromEvent(nil, event)
if err != nil {
return err
}
o.removeMember(member.UserID)
}
o.ObjectRoot.AppendEvent(event)
return nil
}
func (o *Org) setData(event *es_models.Event) error {
err := json.Unmarshal(event.Data, o)
if err != nil {
return errors.ThrowInternal(err, "EVENT-BpbQZ", "unable to unmarshal event")
}
return nil
}
func (o *Org) getMember(userID string) *OrgMember {
for _, member := range o.Members {
if member.UserID == userID {
return member
}
}
return nil
}
func (o *Org) setMember(member *OrgMember) {
for i, existingMember := range o.Members {
if existingMember.UserID == member.UserID {
o.Members[i] = member
return
}
}
o.Members = append(o.Members, member)
}
func (o *Org) removeMember(userID string) {
for i := len(o.Members) - 1; i >= 0; i-- {
if o.Members[i].UserID == userID {
copy(o.Members[i:], o.Members[i+1:])
o.Members[len(o.Members)-1] = nil
o.Members = o.Members[:len(o.Members)-1]
}
}
}
func (o *Org) Changes(changed *Org) map[string]interface{} {
changes := make(map[string]interface{}, 2)
if changed.Name != "" && changed.Name != o.Name {
changes["name"] = changed.Name
}
if changed.Domain != "" && changed.Domain != o.Domain {
changes["domain"] = changed.Domain
}
return changes
}

View File

@@ -0,0 +1,170 @@
package model
import (
"encoding/json"
"testing"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/org/model"
)
func TestOrgFromEvents(t *testing.T) {
type args struct {
event []*es_models.Event
org *Org
}
tests := []struct {
name string
args args
result *Org
}{
{
name: "org from events, ok",
args: args{
event: []*es_models.Event{
{AggregateID: "ID", Sequence: 1, Type: OrgAdded},
},
org: &Org{Name: "OrgName"},
},
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE), Name: "OrgName"},
},
{
name: "org from events, nil org",
args: args{
event: []*es_models.Event{
{AggregateID: "ID", Sequence: 1, Type: OrgAdded},
},
org: nil,
},
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE)},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.org != nil {
data, _ := json.Marshal(tt.args.org)
tt.args.event[0].Data = data
}
result, _ := OrgFromEvents(tt.args.org, tt.args.event...)
if result.Name != tt.result.Name {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.result.Name, result.Name)
}
})
}
}
func TestAppendEvent(t *testing.T) {
type args struct {
event *es_models.Event
org *Org
}
tests := []struct {
name string
args args
result *Org
}{
{
name: "append added event",
args: args{
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: OrgAdded},
org: &Org{Name: "OrgName"},
},
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE), Name: "OrgName"},
},
{
name: "append change event",
args: args{
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: OrgChanged, Data: []byte(`{"domain": "OrgDomain"}`)},
org: &Org{Name: "OrgName", Domain: "asdf"},
},
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE), Name: "OrgName", Domain: "OrgDomain"},
},
{
name: "append deactivate event",
args: args{
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: OrgDeactivated},
},
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_INACTIVE)},
},
{
name: "append reactivate event",
args: args{
event: &es_models.Event{AggregateID: "ID", Sequence: 1, Type: OrgReactivated},
},
result: &Org{ObjectRoot: es_models.ObjectRoot{AggregateID: "ID"}, State: int32(model.ORGSTATE_ACTIVE)},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.org != nil {
data, _ := json.Marshal(tt.args.org)
tt.args.event.Data = data
}
result := &Org{}
result.AppendEvent(tt.args.event)
if result.State != tt.result.State {
t.Errorf("got wrong result state: expected: %v, actual: %v ", tt.result.State, result.State)
}
if result.Name != tt.result.Name {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.result.Name, result.Name)
}
if result.ObjectRoot.AggregateID != tt.result.ObjectRoot.AggregateID {
t.Errorf("got wrong result id: expected: %v, actual: %v ", tt.result.ObjectRoot.AggregateID, result.ObjectRoot.AggregateID)
}
})
}
}
func TestChanges(t *testing.T) {
type args struct {
existing *Org
new *Org
}
type res struct {
changesLen int
}
tests := []struct {
name string
args args
res res
}{
{
name: "org name changes",
args: args{
existing: &Org{Name: "Name"},
new: &Org{Name: "NameChanged"},
},
res: res{
changesLen: 1,
},
},
{
name: "org domain changes",
args: args{
existing: &Org{Name: "Name", Domain: "old domain"},
new: &Org{Name: "Name", Domain: "new domain"},
},
res: res{
changesLen: 1,
},
},
{
name: "no changes",
args: args{
existing: &Org{Name: "Name"},
new: &Org{Name: "Name"},
},
res: res{
changesLen: 0,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
changes := tt.args.existing.Changes(tt.args.new)
if len(changes) != tt.res.changesLen {
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
}
})
}
}

View File

@@ -0,0 +1,25 @@
package model
import "github.com/caos/zitadel/internal/eventstore/models"
const (
OrgAggregate models.AggregateType = "org"
OrgDomainAggregate models.AggregateType = "org.domain"
OrgNameAggregate models.AggregateType = "org.name"
OrgAdded models.EventType = "org.added"
OrgChanged models.EventType = "org.changed"
OrgDeactivated models.EventType = "org.deactivated"
OrgReactivated models.EventType = "org.reactivated"
OrgRemoved models.EventType = "org.removed"
OrgNameReserved models.EventType = "org.name.reserved"
OrgNameReleased models.EventType = "org.name.released"
OrgDomainReserved models.EventType = "org.domain.reserved"
OrgDomainReleased models.EventType = "org.domain.released"
OrgMemberAdded models.EventType = "org.member.added"
OrgMemberChanged models.EventType = "org.member.changed"
OrgMemberRemoved models.EventType = "org.member.removed"
)