mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 04:07:31 +00:00
cherry pick changes from main (#3371)
* feat: remove exif data from uploaded images (#3221) * feat: remove exif tags from images * feat: remove exif data * feat: remove exif * fix: add preferredLoginName to user grant response (#3271) * chore: log webauthn parse error (#3272) * log error * log error * feat: Help link in privacy policy * fix: convert correct detail data on organization (#3279) * fix: handle empty editor users * fix: add some missing translations (#3291) * fix: org policy translations * fix: metadata event types translation * fix: translations * fix: filter resource owner correctly on project grant members (#3281) * fix: filter resource owner correctly on project grant members * fix: filter resource owner correctly on project grant members * fix: add orgIDs to zitadel permissions request Co-authored-by: fabi <fabienne.gerschwiler@gmail.com> * fix: get IAM memberships correctly in MyZitadelPermissions (#3309) * fix: correct login names on auth and notification users (#3349) * fix: correct login names on auth and notification users * fix: migration * fix: handle resource owner in action flows (#3361) * fix merge * fix: exchange exif library (#3366) * fix: exchange exif library * ignore tiffs * requested fixes * feat: Help link in privacy policy Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@@ -11,6 +13,7 @@ import (
|
||||
"github.com/caos/logging"
|
||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/superseriousbusiness/exifremove/pkg/exifremove"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
@@ -136,7 +139,13 @@ func UploadHandleFunc(s AssetsService, uploader Uploader) func(http.ResponseWrit
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, file, size)
|
||||
cleanedFile, cleanedSize, err := removeExif(file, size, contentType)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("remove exif error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, cleanedFile, cleanedSize)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
@@ -191,3 +200,25 @@ func DownloadHandleFunc(s AssetsService, downloader Downloader) func(http.Respon
|
||||
w.Write(data)
|
||||
}
|
||||
}
|
||||
|
||||
func removeExif(file io.Reader, size int64, contentType string) (io.Reader, int64, error) {
|
||||
if !isAllowedContentType(contentType) {
|
||||
return file, size, nil
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := buf.ReadFrom(file)
|
||||
if err != nil {
|
||||
return file, 0, err
|
||||
}
|
||||
data, err := exifremove.Remove(buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return bytes.NewReader(data), int64(len(data)), nil
|
||||
}
|
||||
|
||||
func isAllowedContentType(contentType string) bool {
|
||||
return strings.HasSuffix(contentType, "png") ||
|
||||
strings.HasSuffix(contentType, "jpg") ||
|
||||
strings.HasSuffix(contentType, "jpeg")
|
||||
}
|
||||
|
@@ -9,5 +9,6 @@ func UpdatePrivacyPolicyToDomain(req *admin_pb.UpdatePrivacyPolicyRequest) *doma
|
||||
return &domain.PrivacyPolicy{
|
||||
TOSLink: req.TosLink,
|
||||
PrivacyLink: req.PrivacyLink,
|
||||
HelpLink: req.HelpLink,
|
||||
}
|
||||
}
|
||||
|
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) ListMyZitadelPermissions(ctx context.Context, _ *auth_pb.ListMyZitadelPermissionsRequest) (*auth_pb.ListMyZitadelPermissionsResponse, error) {
|
||||
perms, err := s.query.MyZitadelPermissions(ctx, authz.GetCtxData(ctx).UserID)
|
||||
perms, err := s.query.MyZitadelPermissions(ctx, authz.GetCtxData(ctx).OrgID, authz.GetCtxData(ctx).UserID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -168,6 +168,7 @@ func (s *Server) ListMyProjectOrgs(ctx context.Context, req *auth_pb.ListMyProje
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantProjectID, userGrantUserID}})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -9,6 +9,7 @@ func AddPrivacyPolicyToDomain(req *mgmt_pb.AddCustomPrivacyPolicyRequest) *domai
|
||||
return &domain.PrivacyPolicy{
|
||||
TOSLink: req.TosLink,
|
||||
PrivacyLink: req.PrivacyLink,
|
||||
HelpLink: req.HelpLink,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,5 +17,6 @@ func UpdatePrivacyPolicyToDomain(req *mgmt_pb.UpdateCustomPrivacyPolicyRequest)
|
||||
return &domain.PrivacyPolicy{
|
||||
TOSLink: req.TosLink,
|
||||
PrivacyLink: req.PrivacyLink,
|
||||
HelpLink: req.HelpLink,
|
||||
}
|
||||
}
|
||||
|
@@ -126,11 +126,6 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ownerQuery, err := query.NewMemberResourceOwnerSearchQuery(authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries = append(queries, ownerQuery)
|
||||
return &query.ProjectGrantMembersQuery{
|
||||
MembersQuery: query.MembersQuery{
|
||||
SearchRequest: query.SearchRequest{
|
||||
@@ -143,6 +138,7 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis
|
||||
},
|
||||
ProjectID: req.ProjectId,
|
||||
GrantID: req.GrantId,
|
||||
OrgID: authz.GetCtxData(ctx).OrgID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@@ -88,7 +88,7 @@ func OrgToPb(org *query.Org) *org_pb.Org {
|
||||
Id: org.ID,
|
||||
Name: org.Name,
|
||||
PrimaryDomain: org.Domain,
|
||||
Details: object.AddToDetailsPb(org.Sequence, org.CreationDate, org.ResourceOwner),
|
||||
Details: object.ToViewDetailsPb(org.Sequence, org.CreationDate, org.ChangeDate, org.ResourceOwner),
|
||||
State: OrgStateToPb(org.State),
|
||||
}
|
||||
}
|
||||
|
@@ -11,6 +11,7 @@ func ModelPrivacyPolicyToPb(policy *query.PrivacyPolicy) *policy_pb.PrivacyPolic
|
||||
IsDefault: policy.IsDefault,
|
||||
TosLink: policy.TOSLink,
|
||||
PrivacyLink: policy.PrivacyLink,
|
||||
HelpLink: policy.HelpLink,
|
||||
Details: object.ToViewDetailsPb(
|
||||
policy.Sequence,
|
||||
policy.CreationDate,
|
||||
|
@@ -457,7 +457,6 @@ func FooterTextToPb(text domain.FooterText) *text_pb.FooterText {
|
||||
Tos: text.TOS,
|
||||
PrivacyPolicy: text.PrivacyPolicy,
|
||||
Help: text.Help,
|
||||
HelpLink: text.HelpLink,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -947,6 +946,5 @@ func FooterTextPbToDomain(text *text_pb.FooterText) domain.FooterText {
|
||||
TOS: text.Tos,
|
||||
PrivacyPolicy: text.PrivacyPolicy,
|
||||
Help: text.Help,
|
||||
HelpLink: text.HelpLink,
|
||||
}
|
||||
}
|
||||
|
@@ -21,22 +21,23 @@ func UserGrantsToPb(assetPrefix string, grants []*query.UserGrant) []*user_pb.Us
|
||||
|
||||
func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGrant {
|
||||
return &user_pb.UserGrant{
|
||||
Id: grant.ID,
|
||||
UserId: grant.UserID,
|
||||
State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE,
|
||||
RoleKeys: grant.Roles,
|
||||
ProjectId: grant.ProjectID,
|
||||
OrgId: grant.ResourceOwner,
|
||||
ProjectGrantId: grant.GrantID,
|
||||
UserName: grant.Username,
|
||||
FirstName: grant.FirstName,
|
||||
LastName: grant.LastName,
|
||||
Email: grant.Email,
|
||||
DisplayName: grant.DisplayName,
|
||||
OrgDomain: grant.OrgPrimaryDomain,
|
||||
OrgName: grant.OrgName,
|
||||
ProjectName: grant.ProjectName,
|
||||
AvatarUrl: domain.AvatarURL(assetPrefix, grant.UserResourceOwner, grant.AvatarURL),
|
||||
Id: grant.ID,
|
||||
UserId: grant.UserID,
|
||||
State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE,
|
||||
RoleKeys: grant.Roles,
|
||||
ProjectId: grant.ProjectID,
|
||||
OrgId: grant.ResourceOwner,
|
||||
ProjectGrantId: grant.GrantID,
|
||||
UserName: grant.Username,
|
||||
FirstName: grant.FirstName,
|
||||
LastName: grant.LastName,
|
||||
Email: grant.Email,
|
||||
DisplayName: grant.DisplayName,
|
||||
OrgDomain: grant.OrgPrimaryDomain,
|
||||
OrgName: grant.OrgName,
|
||||
ProjectName: grant.ProjectName,
|
||||
AvatarUrl: domain.AvatarURL(assetPrefix, grant.UserResourceOwner, grant.AvatarURL),
|
||||
PreferredLoginName: grant.PreferredLoginName,
|
||||
Details: object.ToViewDetailsPb(
|
||||
grant.Sequence,
|
||||
grant.CreationDate,
|
||||
|
@@ -95,7 +95,7 @@ func (o *OPStorage) CreateAccessToken(ctx context.Context, req op.TokenRequest)
|
||||
applicationID = authReq.ApplicationID
|
||||
userOrgID = authReq.UserOrgID
|
||||
}
|
||||
resp, err := o.command.AddUserToken(ctx, userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
|
||||
resp, err := o.command.AddUserToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(), req.GetAudience(), req.GetScopes(), o.defaultAccessTokenLifetime) //PLANNED: lifetime from client
|
||||
if err != nil {
|
||||
return "", time.Time{}, err
|
||||
}
|
||||
@@ -123,7 +123,7 @@ func (o *OPStorage) CreateAccessAndRefreshTokens(ctx context.Context, req op.Tok
|
||||
if request, ok := req.(op.RefreshTokenRequest); ok {
|
||||
request.SetCurrentScopes(scopes)
|
||||
}
|
||||
resp, token, err := o.command.AddAccessAndRefreshToken(ctx, userOrgID, userAgentID, applicationID, req.GetSubject(),
|
||||
resp, token, err := o.command.AddAccessAndRefreshToken(setContextUserSystem(ctx), userOrgID, userAgentID, applicationID, req.GetSubject(),
|
||||
refreshToken, req.GetAudience(), scopes, authMethodsReferences, o.defaultAccessTokenLifetime,
|
||||
o.defaultRefreshTokenIdleExpiration, o.defaultRefreshTokenExpiration, authTime) //PLANNED: lifetime from client
|
||||
if err != nil {
|
||||
@@ -171,7 +171,10 @@ func (o *OPStorage) TerminateSession(ctx context.Context, userID, clientID strin
|
||||
if len(userIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
err = o.command.HumansSignOut(ctx, userAgentID, userIDs)
|
||||
data := authz.CtxData{
|
||||
UserID: userID,
|
||||
}
|
||||
err = o.command.HumansSignOut(authz.SetCtxData(ctx, data), userAgentID, userIDs)
|
||||
logging.Log("OIDC-Dggt2").OnError(err).Error("error signing out")
|
||||
return err
|
||||
}
|
||||
@@ -255,3 +258,10 @@ func (o *OPStorage) assertClientScopesForPAT(ctx context.Context, token *model.T
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setContextUserSystem(ctx context.Context) context.Context {
|
||||
data := authz.CtxData{
|
||||
UserID: "SYSTEM",
|
||||
}
|
||||
return authz.SetCtxData(ctx, data)
|
||||
}
|
||||
|
@@ -203,7 +203,7 @@ func (l *Login) handleExternalUserAuthenticated(w http.ResponseWriter, r *http.R
|
||||
}
|
||||
|
||||
instanceID := authz.GetInstance(r.Context()).ID
|
||||
err = l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, userAgentID, instanceID, externalUser, domain.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, userAgentID, instanceID, externalUser, domain.BrowserInfoFromRequest(r))
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
err = nil
|
||||
|
@@ -84,7 +84,7 @@ func (l *Login) handleJWTExtraction(w http.ResponseWriter, r *http.Request, auth
|
||||
return
|
||||
}
|
||||
metadata := externalUser.Metadatas
|
||||
err = l.authRepo.CheckExternalUserLogin(r.Context(), authReq.ID, authReq.AgentID, authReq.InstanceID, externalUser, domain.BrowserInfoFromRequest(r))
|
||||
err = l.authRepo.CheckExternalUserLogin(setContext(r.Context(), ""), authReq.ID, authReq.AgentID, authReq.InstanceID, externalUser, domain.BrowserInfoFromRequest(r))
|
||||
if err != nil {
|
||||
l.jwtExtractionUserNotFound(w, r, authReq, idpConfig, tokens, err)
|
||||
return
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/i18n"
|
||||
"github.com/caos/zitadel/internal/notification/templates"
|
||||
"github.com/caos/zitadel/internal/renderer"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
@@ -30,6 +31,10 @@ type Renderer struct {
|
||||
staticStorage static.Storage
|
||||
}
|
||||
|
||||
type LanguageData struct {
|
||||
Lang string
|
||||
}
|
||||
|
||||
func CreateRenderer(pathPrefix string, staticDir http.FileSystem, staticStorage static.Storage, cookieName string, defaultLanguage language.Tag) *Renderer {
|
||||
r := &Renderer{
|
||||
pathPrefix: pathPrefix,
|
||||
@@ -345,24 +350,23 @@ func (l *Login) getBaseData(r *http.Request, authReq *domain.AuthRequest, title
|
||||
CSRF: csrf.TemplateField(r),
|
||||
Nonce: http_mw.GetNonce(r),
|
||||
}
|
||||
var privacyPolicy *domain.PrivacyPolicy
|
||||
if authReq != nil {
|
||||
baseData.LoginPolicy = authReq.LoginPolicy
|
||||
baseData.LabelPolicy = authReq.LabelPolicy
|
||||
baseData.IDPProviders = authReq.AllowedExternalIDPs
|
||||
if authReq.PrivacyPolicy != nil {
|
||||
baseData.TOSLink = authReq.PrivacyPolicy.TOSLink
|
||||
baseData.PrivacyLink = authReq.PrivacyPolicy.PrivacyLink
|
||||
if authReq.PrivacyPolicy == nil {
|
||||
return baseData
|
||||
}
|
||||
privacyPolicy = authReq.PrivacyPolicy
|
||||
} else {
|
||||
privacyPolicy, err := l.query.DefaultPrivacyPolicy(r.Context())
|
||||
policy, err := l.query.DefaultPrivacyPolicy(r.Context())
|
||||
if err != nil {
|
||||
return baseData
|
||||
}
|
||||
if privacyPolicy != nil {
|
||||
baseData.TOSLink = privacyPolicy.TOSLink
|
||||
baseData.PrivacyLink = privacyPolicy.PrivacyLink
|
||||
}
|
||||
privacyPolicy = policy.ToDomain()
|
||||
}
|
||||
baseData = l.setLinksOnBaseData(baseData, privacyPolicy)
|
||||
return baseData
|
||||
}
|
||||
|
||||
@@ -392,6 +396,26 @@ func (l *Login) getProfileData(authReq *domain.AuthRequest) profileData {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Login) setLinksOnBaseData(baseData baseData, privacyPolicy *domain.PrivacyPolicy) baseData {
|
||||
lang := LanguageData{
|
||||
Lang: baseData.Lang,
|
||||
}
|
||||
baseData.TOSLink = privacyPolicy.TOSLink
|
||||
baseData.PrivacyLink = privacyPolicy.PrivacyLink
|
||||
baseData.HelpLink = privacyPolicy.HelpLink
|
||||
|
||||
if link, err := templates.ParseTemplateText(privacyPolicy.TOSLink, lang); err == nil {
|
||||
baseData.TOSLink = link
|
||||
}
|
||||
if link, err := templates.ParseTemplateText(privacyPolicy.PrivacyLink, lang); err == nil {
|
||||
baseData.PrivacyLink = link
|
||||
}
|
||||
if link, err := templates.ParseTemplateText(privacyPolicy.HelpLink, lang); err == nil {
|
||||
baseData.HelpLink = link
|
||||
}
|
||||
return baseData
|
||||
}
|
||||
|
||||
func (l *Login) getErrorMessage(r *http.Request, err error) (errID, errMsg string) {
|
||||
caosErr := new(caos_errs.CaosError)
|
||||
if errors.As(err, &caosErr) {
|
||||
@@ -519,6 +543,7 @@ type baseData struct {
|
||||
DisplayLoginNameSuffix bool
|
||||
TOSLink string
|
||||
PrivacyLink string
|
||||
HelpLink string
|
||||
AuthReqID string
|
||||
CSRF template.HTML
|
||||
Nonce string
|
||||
|
@@ -297,7 +297,6 @@ Footer:
|
||||
Tos: AGB
|
||||
PrivacyPolicy: Datenschutzerklärung
|
||||
Help: Hilfe
|
||||
HelpLink: https://docs.zitadel.ch/docs/manuals/user-login
|
||||
|
||||
Errors:
|
||||
Internal: Es ist ein interner Fehler aufgetreten
|
||||
|
@@ -298,7 +298,6 @@ Footer:
|
||||
Tos: TOS
|
||||
PrivacyPolicy: Privacy policy
|
||||
Help: Help
|
||||
HelpLink: https://docs.zitadel.ch/docs/manuals/user-login
|
||||
|
||||
Errors:
|
||||
Internal: An internal error occured
|
||||
|
@@ -298,7 +298,6 @@ Footer:
|
||||
Tos: Termini di servizio
|
||||
PrivacyPolicy: l'informativa sulla privacy
|
||||
Help: Aiuto
|
||||
HelpLink: 'https://docs.zitadel.ch/docs/manuals/user-login'
|
||||
|
||||
Errors:
|
||||
Internal: Si è verificato un errore interno
|
||||
|
@@ -13,6 +13,8 @@
|
||||
{{ if .PrivacyLink }}
|
||||
<a href="{{.PrivacyLink}}" rel="noopener noreferrer" target="_blank" alt="Privacy Policy">{{t "Footer.PrivacyPolicy"}}</a>
|
||||
{{end}}
|
||||
<a href="{{t "Footer.HelpLink"}}" target="_black" alt="Help">{{t "Footer.Help"}}</a>
|
||||
{{ if .HelpLink }}
|
||||
<a href="{{.HelpLink}}" rel="noopener noreferrer" target="_blank" alt="Help">{{t "Footer.Help"}}</a>
|
||||
{{end}}
|
||||
</footer>
|
||||
{{end}}
|
||||
|
Reference in New Issue
Block a user