mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:27:42 +00:00
feat: actions v2 for functions (#9420)
# Which Problems Are Solved Actions v2 are not executed in different functions, as provided by the actions v1. # How the Problems Are Solved Add functionality to call actions v2 through OIDC and SAML logic to complement tokens and SAMLResponses. # Additional Changes - Corrected testing for retrieved intent information - Added testing for IDP types - Corrected handling of context for issuer in SAML logic # Additional Context - Closes #7247 - Dependent on https://github.com/zitadel/saml/pull/97 - docs for migration are done in separate issue: https://github.com/zitadel/zitadel/issues/9456 --------- Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package saml
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/zitadel/saml/pkg/provider"
|
||||
@@ -32,9 +33,16 @@ func (p *Provider) CreateResponse(ctx context.Context, authReq models.AuthReques
|
||||
RelayState: authReq.GetRelayState(),
|
||||
AcsUrl: authReq.GetAccessConsumerServiceURL(),
|
||||
RequestID: authReq.GetAuthRequestID(),
|
||||
Issuer: authReq.GetDestination(),
|
||||
Audience: authReq.GetIssuer(),
|
||||
}
|
||||
|
||||
issuer := ContextToIssuer(ctx)
|
||||
req, err := http.NewRequestWithContext(provider.ContextWithIssuer(ctx, issuer), http.MethodGet, issuer, nil)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
resp.Issuer = p.GetEntityID(req)
|
||||
|
||||
samlResponse, err := p.AuthCallbackResponse(ctx, authReq, resp)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package saml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
@@ -83,7 +84,7 @@ func NewProvider(
|
||||
|
||||
p, err := provider.NewProvider(
|
||||
provStorage,
|
||||
HandlerPrefix,
|
||||
IssuerFromContext,
|
||||
conf.ProviderConfig,
|
||||
options...,
|
||||
)
|
||||
@@ -96,6 +97,16 @@ func NewProvider(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ContextToIssuer(ctx context.Context) string {
|
||||
return http_utils.DomainContext(ctx).Origin() + HandlerPrefix
|
||||
}
|
||||
|
||||
func IssuerFromContext(_ bool) (provider.IssuerFromRequest, error) {
|
||||
return func(r *http.Request) string {
|
||||
return ContextToIssuer(r.Context())
|
||||
}, nil
|
||||
}
|
||||
|
||||
func newStorage(
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
|
@@ -3,6 +3,7 @@ package saml
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -26,7 +27,9 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/handler/crdb"
|
||||
"github.com/zitadel/zitadel/internal/execution"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
exec_repo "github.com/zitadel/zitadel/internal/repository/execution"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
)
|
||||
@@ -37,7 +40,8 @@ var _ provider.AuthStorage = &Storage{}
|
||||
var _ provider.UserStorage = &Storage{}
|
||||
|
||||
const (
|
||||
LoginClientHeader = "x-zitadel-login-client"
|
||||
LoginClientHeader = "x-zitadel-login-client"
|
||||
AttributeActionLogFormat = "urn:zitadel:iam:action:%s:log"
|
||||
)
|
||||
|
||||
type Storage struct {
|
||||
@@ -380,9 +384,86 @@ func (p *Storage) getCustomAttributes(ctx context.Context, user *query.User, use
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
function := exec_repo.ID(domain.ExecutionTypeFunction, domain.ActionFunctionPreSAMLResponse.LocalizationKey())
|
||||
executionTargets, err := execution.QueryExecutionTargetsForFunction(ctx, p.query, function)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// correct time for utc
|
||||
user.CreationDate = user.CreationDate.UTC()
|
||||
user.ChangeDate = user.ChangeDate.UTC()
|
||||
|
||||
info := &ContextInfo{
|
||||
Function: function,
|
||||
User: user,
|
||||
UserGrants: userGrants.UserGrants,
|
||||
}
|
||||
|
||||
resp, err := execution.CallTargets(ctx, executionTargets, info)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
contextInfoResponse, ok := resp.(*ContextInfoResponse)
|
||||
if !ok || contextInfoResponse == nil {
|
||||
return customAttributes, nil
|
||||
}
|
||||
attributeLogs := make([]string, 0)
|
||||
for _, metadata := range contextInfoResponse.SetUserMetadata {
|
||||
if _, err = p.command.SetUserMetadata(ctx, metadata, user.ID, user.ResourceOwner); err != nil {
|
||||
attributeLogs = append(attributeLogs, fmt.Sprintf("failed to set user metadata key %q", metadata.Key))
|
||||
}
|
||||
}
|
||||
for _, attribute := range contextInfoResponse.AppendAttribute {
|
||||
customAttributes = appendCustomAttribute(customAttributes, attribute.Name, attribute.NameFormat, attribute.Value)
|
||||
}
|
||||
if len(attributeLogs) > 0 {
|
||||
customAttributes = appendCustomAttribute(customAttributes, fmt.Sprintf(AttributeActionLogFormat, function), "", attributeLogs)
|
||||
}
|
||||
return customAttributes, nil
|
||||
}
|
||||
|
||||
type ContextInfo struct {
|
||||
Function string `json:"function,omitempty"`
|
||||
User *query.User `json:"user,omitempty"`
|
||||
UserGrants []*query.UserGrant `json:"user_grants,omitempty"`
|
||||
Response *ContextInfoResponse `json:"response,omitempty"`
|
||||
}
|
||||
|
||||
type ContextInfoResponse struct {
|
||||
SetUserMetadata []*domain.Metadata `json:"set_user_metadata,omitempty"`
|
||||
AppendAttribute []*AppendAttribute `json:"append_attribute,omitempty"`
|
||||
}
|
||||
|
||||
type AppendAttribute struct {
|
||||
Name string `json:"name"`
|
||||
NameFormat string `json:"name_format"`
|
||||
Value []string `json:"value"`
|
||||
}
|
||||
|
||||
func (c *ContextInfo) GetHTTPRequestBody() []byte {
|
||||
data, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (c *ContextInfo) SetHTTPResponseBody(resp []byte) error {
|
||||
if !json.Valid(resp) {
|
||||
return zerrors.ThrowPreconditionFailed(nil, "ACTION-4m9s2", "Errors.Execution.ResponseIsNotValidJSON")
|
||||
}
|
||||
if c.Response == nil {
|
||||
c.Response = &ContextInfoResponse{}
|
||||
}
|
||||
return json.Unmarshal(resp, c.Response)
|
||||
}
|
||||
|
||||
func (c *ContextInfo) GetContent() interface{} {
|
||||
return c.Response
|
||||
}
|
||||
|
||||
func (p *Storage) getGrants(ctx context.Context, userID, applicationID string) (*query.UserGrants, error) {
|
||||
projectID, err := p.query.ProjectIDFromClientID(ctx, applicationID)
|
||||
if err != nil {
|
||||
|
Reference in New Issue
Block a user