mirror of
https://github.com/zitadel/zitadel.git
synced 2025-10-23 20:41:56 +00:00
Merge branch 'main' into perf-introspecion
This commit is contained in:
@@ -63,7 +63,7 @@ func New(
|
||||
}
|
||||
|
||||
api.grpcServer = server.CreateServer(api.verifier, authZ, queries, http2HostName, tlsConfig, accessInterceptor.AccessService())
|
||||
api.grpcGateway, err = server.CreateGateway(ctx, port, http1HostName, accessInterceptor)
|
||||
api.grpcGateway, err = server.CreateGateway(ctx, port, http1HostName, accessInterceptor, tlsConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -80,7 +80,7 @@ func New(
|
||||
// creates a new grpc gateway and registers it as a separate http handler
|
||||
//
|
||||
// used for v1 api (system, admin, mgmt, auth)
|
||||
func (a *API) RegisterServer(ctx context.Context, grpcServer server.WithGatewayPrefix) error {
|
||||
func (a *API) RegisterServer(ctx context.Context, grpcServer server.WithGatewayPrefix, tlsConfig *tls.Config) error {
|
||||
grpcServer.RegisterServer(a.grpcServer)
|
||||
handler, prefix, err := server.CreateGatewayWithPrefix(
|
||||
ctx,
|
||||
@@ -89,6 +89,7 @@ func (a *API) RegisterServer(ctx context.Context, grpcServer server.WithGatewayP
|
||||
a.http1HostName,
|
||||
a.accessInterceptor,
|
||||
a.queries,
|
||||
tlsConfig,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -60,7 +60,6 @@ func eventRequestToFilter(ctx context.Context, req *admin_pb.ListEventsRequest)
|
||||
AwaitOpenTransactions().
|
||||
ResourceOwner(req.ResourceOwner).
|
||||
EditorUser(req.EditorUserId).
|
||||
CreationDateAfter(req.CreationDate.AsTime()).
|
||||
SequenceGreater(req.Sequence)
|
||||
|
||||
if len(aggregateIDs) > 0 || len(aggregateTypes) > 0 || len(eventTypes) > 0 {
|
||||
@@ -71,8 +70,11 @@ func eventRequestToFilter(ctx context.Context, req *admin_pb.ListEventsRequest)
|
||||
Builder()
|
||||
}
|
||||
|
||||
if req.Asc {
|
||||
if req.GetAsc() {
|
||||
builder.OrderAsc()
|
||||
builder.CreationDateAfter(req.CreationDate.AsTime())
|
||||
} else {
|
||||
builder.CreationDateBefore(req.CreationDate.AsTime())
|
||||
}
|
||||
|
||||
return builder, nil
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package event
|
||||
|
||||
import (
|
||||
structpb "github.com/golang/protobuf/ptypes/struct"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
|
||||
func ListUsersRequestToModel(req *mgmt_pb.ListUsersRequest) (*query.UserSearchQueries, error) {
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
queries, err := user_grpc.UserQueriesToQuery(req.Queries)
|
||||
queries, err := user_grpc.UserQueriesToQuery(req.Queries, 0 /*start from level 0*/)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
|
||||
"github.com/zitadel/logging"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
healthpb "google.golang.org/grpc/health/grpc_health_v1"
|
||||
"google.golang.org/protobuf/encoding/protojson"
|
||||
@@ -89,10 +91,11 @@ func CreateGatewayWithPrefix(
|
||||
http1HostName string,
|
||||
accessInterceptor *http_mw.AccessInterceptor,
|
||||
queries *query.Queries,
|
||||
tlsConfig *tls.Config,
|
||||
) (http.Handler, string, error) {
|
||||
runtimeMux := runtime.NewServeMux(serveMuxOptions...)
|
||||
opts := []grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithTransportCredentials(grpcCredentials(tlsConfig)),
|
||||
grpc.WithUnaryInterceptor(client_middleware.DefaultTracingClient()),
|
||||
}
|
||||
connection, err := dial(ctx, port, opts)
|
||||
@@ -106,11 +109,17 @@ func CreateGatewayWithPrefix(
|
||||
return addInterceptors(runtimeMux, http1HostName, accessInterceptor, queries), g.GatewayPathPrefix(), nil
|
||||
}
|
||||
|
||||
func CreateGateway(ctx context.Context, port uint16, http1HostName string, accessInterceptor *http_mw.AccessInterceptor) (*Gateway, error) {
|
||||
func CreateGateway(
|
||||
ctx context.Context,
|
||||
port uint16,
|
||||
http1HostName string,
|
||||
accessInterceptor *http_mw.AccessInterceptor,
|
||||
tlsConfig *tls.Config,
|
||||
) (*Gateway, error) {
|
||||
connection, err := dial(ctx,
|
||||
port,
|
||||
[]grpc.DialOption{
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
grpc.WithTransportCredentials(grpcCredentials(tlsConfig)),
|
||||
grpc.WithUnaryInterceptor(client_middleware.DefaultTracingClient()),
|
||||
})
|
||||
if err != nil {
|
||||
@@ -217,3 +226,15 @@ func (r *cookieResponseWriter) WriteHeader(status int) {
|
||||
}
|
||||
r.ResponseWriter.WriteHeader(status)
|
||||
}
|
||||
|
||||
func grpcCredentials(tlsConfig *tls.Config) credentials.TransportCredentials {
|
||||
creds := insecure.NewCredentials()
|
||||
if tlsConfig != nil {
|
||||
tlsConfigClone := tlsConfig.Clone()
|
||||
// We don't want to verify the certificate of the internal grpc server
|
||||
// That's up to the client who called the gRPC gateway
|
||||
tlsConfigClone.InsecureSkipVerify = true
|
||||
creds = credentials.NewTLS(tlsConfigClone)
|
||||
}
|
||||
return creds
|
||||
}
|
||||
|
||||
@@ -3,12 +3,11 @@ package server
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
structpb "github.com/golang/protobuf/ptypes/struct"
|
||||
"github.com/zitadel/logging"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/proto"
|
||||
"github.com/zitadel/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
@@ -22,24 +21,23 @@ func NewValidator(validations map[string]ValidationFunction) *Validator {
|
||||
return &Validator{validations: validations}
|
||||
}
|
||||
|
||||
func (v *Validator) Healthz(_ context.Context, e *empty.Empty) (*empty.Empty, error) {
|
||||
func (v *Validator) Healthz(_ context.Context, e *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
return e, nil
|
||||
}
|
||||
|
||||
func (v *Validator) Ready(ctx context.Context, e *empty.Empty) (*empty.Empty, error) {
|
||||
func (v *Validator) Ready(ctx context.Context, e *emptypb.Empty) (*emptypb.Empty, error) {
|
||||
if len(validate(ctx, v.validations)) == 0 {
|
||||
return e, nil
|
||||
}
|
||||
return nil, errors.ThrowInternal(nil, "API-2jD9a", "not ready")
|
||||
}
|
||||
|
||||
func (v *Validator) Validate(ctx context.Context, _ *empty.Empty) (*structpb.Struct, error) {
|
||||
validations := validate(ctx, v.validations)
|
||||
return proto.ToPBStruct(validations)
|
||||
func (v *Validator) Validate(ctx context.Context, _ *emptypb.Empty) (*structpb.Struct, error) {
|
||||
return structpb.NewStruct(validate(ctx, v.validations))
|
||||
}
|
||||
|
||||
func validate(ctx context.Context, validations map[string]ValidationFunction) map[string]error {
|
||||
errors := make(map[string]error)
|
||||
func validate(ctx context.Context, validations map[string]ValidationFunction) map[string]any {
|
||||
errors := make(map[string]any)
|
||||
for id, validation := range validations {
|
||||
if err := validation(ctx); err != nil {
|
||||
logging.Log("API-vf823").WithError(err).WithField("traceID", tracing.TraceIDFromCtx(ctx)).Error("validation failed")
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/ptypes/empty"
|
||||
"google.golang.org/protobuf/types/known/emptypb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
)
|
||||
@@ -15,7 +15,7 @@ func TestValidator_Healthz(t *testing.T) {
|
||||
validations map[string]ValidationFunction
|
||||
}
|
||||
type res struct {
|
||||
want *empty.Empty
|
||||
want *emptypb.Empty
|
||||
hasErr bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -27,7 +27,7 @@ func TestValidator_Healthz(t *testing.T) {
|
||||
"ok",
|
||||
fields{},
|
||||
res{
|
||||
&empty.Empty{},
|
||||
&emptypb.Empty{},
|
||||
false,
|
||||
},
|
||||
},
|
||||
@@ -37,7 +37,7 @@ func TestValidator_Healthz(t *testing.T) {
|
||||
v := &Validator{
|
||||
validations: tt.fields.validations,
|
||||
}
|
||||
got, err := v.Healthz(nil, &empty.Empty{})
|
||||
got, err := v.Healthz(context.Background(), &emptypb.Empty{})
|
||||
if (err != nil) != tt.res.hasErr {
|
||||
t.Errorf("Healthz() error = %v, wantErr %v", err, tt.res.hasErr)
|
||||
return
|
||||
@@ -54,7 +54,7 @@ func TestValidator_Ready(t *testing.T) {
|
||||
validations map[string]ValidationFunction
|
||||
}
|
||||
type res struct {
|
||||
want *empty.Empty
|
||||
want *emptypb.Empty
|
||||
hasErr bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -82,7 +82,7 @@ func TestValidator_Ready(t *testing.T) {
|
||||
},
|
||||
}},
|
||||
res{
|
||||
&empty.Empty{},
|
||||
&emptypb.Empty{},
|
||||
false,
|
||||
},
|
||||
},
|
||||
@@ -92,7 +92,7 @@ func TestValidator_Ready(t *testing.T) {
|
||||
v := &Validator{
|
||||
validations: tt.fields.validations,
|
||||
}
|
||||
got, err := v.Ready(context.Background(), &empty.Empty{})
|
||||
got, err := v.Ready(context.Background(), &emptypb.Empty{})
|
||||
if (err != nil) != tt.res.hasErr {
|
||||
t.Errorf("Ready() error = %v, wantErr %v", err, tt.res.hasErr)
|
||||
return
|
||||
@@ -109,7 +109,7 @@ func Test_validate(t *testing.T) {
|
||||
validations map[string]ValidationFunction
|
||||
}
|
||||
type res struct {
|
||||
want map[string]error
|
||||
want map[string]any
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -126,7 +126,7 @@ func Test_validate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
res{
|
||||
map[string]error{},
|
||||
map[string]any{},
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -142,7 +142,7 @@ func Test_validate(t *testing.T) {
|
||||
},
|
||||
},
|
||||
res{
|
||||
map[string]error{
|
||||
map[string]any{
|
||||
"error": errors.ThrowInternal(nil, "id", "message"),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
@@ -17,9 +18,20 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
objpb "github.com/zitadel/zitadel/pkg/grpc/object"
|
||||
session "github.com/zitadel/zitadel/pkg/grpc/session/v2beta"
|
||||
)
|
||||
|
||||
var (
|
||||
timestampComparisons = map[objpb.TimestampQueryMethod]query.TimestampComparison{
|
||||
objpb.TimestampQueryMethod_TIMESTAMP_QUERY_METHOD_EQUALS: query.TimestampEquals,
|
||||
objpb.TimestampQueryMethod_TIMESTAMP_QUERY_METHOD_GREATER: query.TimestampGreater,
|
||||
objpb.TimestampQueryMethod_TIMESTAMP_QUERY_METHOD_GREATER_OR_EQUALS: query.TimestampGreaterOrEquals,
|
||||
objpb.TimestampQueryMethod_TIMESTAMP_QUERY_METHOD_LESS: query.TimestampLess,
|
||||
objpb.TimestampQueryMethod_TIMESTAMP_QUERY_METHOD_LESS_OR_EQUALS: query.TimestampLessOrEquals,
|
||||
}
|
||||
)
|
||||
|
||||
func (s *Server) GetSession(ctx context.Context, req *session.GetSessionRequest) (*session.GetSessionResponse, error) {
|
||||
res, err := s.query.SessionByID(ctx, true, req.GetSessionId(), req.GetSessionToken())
|
||||
if err != nil {
|
||||
@@ -46,7 +58,7 @@ func (s *Server) ListSessions(ctx context.Context, req *session.ListSessionsRequ
|
||||
}
|
||||
|
||||
func (s *Server) CreateSession(ctx context.Context, req *session.CreateSessionRequest) (*session.CreateSessionResponse, error) {
|
||||
checks, metadata, userAgent, err := s.createSessionRequestToCommand(ctx, req)
|
||||
checks, metadata, userAgent, lifetime, err := s.createSessionRequestToCommand(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -55,7 +67,7 @@ func (s *Server) CreateSession(ctx context.Context, req *session.CreateSessionRe
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set, err := s.command.CreateSession(ctx, cmds, metadata, userAgent)
|
||||
set, err := s.command.CreateSession(ctx, cmds, metadata, userAgent, lifetime)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -78,7 +90,7 @@ func (s *Server) SetSession(ctx context.Context, req *session.SetSessionRequest)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
set, err := s.command.UpdateSession(ctx, req.GetSessionId(), req.GetSessionToken(), cmds, req.GetMetadata())
|
||||
set, err := s.command.UpdateSession(ctx, req.GetSessionId(), req.GetSessionToken(), cmds, req.GetMetadata(), req.GetLifetime().AsDuration())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -113,13 +125,14 @@ func sessionsToPb(sessions []*query.Session) []*session.Session {
|
||||
|
||||
func sessionToPb(s *query.Session) *session.Session {
|
||||
return &session.Session{
|
||||
Id: s.ID,
|
||||
CreationDate: timestamppb.New(s.CreationDate),
|
||||
ChangeDate: timestamppb.New(s.ChangeDate),
|
||||
Sequence: s.Sequence,
|
||||
Factors: factorsToPb(s),
|
||||
Metadata: s.Metadata,
|
||||
UserAgent: userAgentToPb(s.UserAgent),
|
||||
Id: s.ID,
|
||||
CreationDate: timestamppb.New(s.CreationDate),
|
||||
ChangeDate: timestamppb.New(s.ChangeDate),
|
||||
Sequence: s.Sequence,
|
||||
Factors: factorsToPb(s),
|
||||
Metadata: s.Metadata,
|
||||
UserAgent: userAgentToPb(s.UserAgent),
|
||||
ExpirationDate: expirationToPb(s.Expiration),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -147,6 +160,13 @@ func userAgentToPb(ua domain.UserAgent) *session.UserAgent {
|
||||
return out
|
||||
}
|
||||
|
||||
func expirationToPb(expiration time.Time) *timestamppb.Timestamp {
|
||||
if expiration.IsZero() {
|
||||
return nil
|
||||
}
|
||||
return timestamppb.New(expiration)
|
||||
}
|
||||
|
||||
func factorsToPb(s *query.Session) *session.Factors {
|
||||
user := userFactorToPb(s.UserFactor)
|
||||
if user == nil {
|
||||
@@ -231,9 +251,10 @@ func listSessionsRequestToQuery(ctx context.Context, req *session.ListSessionsRe
|
||||
}
|
||||
return &query.SessionsSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: fieldNameToSessionColumn(req.GetSortingColumn()),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
@@ -241,8 +262,8 @@ func listSessionsRequestToQuery(ctx context.Context, req *session.ListSessionsRe
|
||||
|
||||
func sessionQueriesToQuery(ctx context.Context, queries []*session.SearchQuery) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries)+1)
|
||||
for i, query := range queries {
|
||||
q[i], err = sessionQueryToQuery(query)
|
||||
for i, v := range queries {
|
||||
q[i], err = sessionQueryToQuery(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -255,10 +276,14 @@ func sessionQueriesToQuery(ctx context.Context, queries []*session.SearchQuery)
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func sessionQueryToQuery(query *session.SearchQuery) (query.SearchQuery, error) {
|
||||
switch q := query.Query.(type) {
|
||||
func sessionQueryToQuery(sq *session.SearchQuery) (query.SearchQuery, error) {
|
||||
switch q := sq.Query.(type) {
|
||||
case *session.SearchQuery_IdsQuery:
|
||||
return idsQueryToQuery(q.IdsQuery)
|
||||
case *session.SearchQuery_UserIdQuery:
|
||||
return query.NewUserIDSearchQuery(q.UserIdQuery.GetId())
|
||||
case *session.SearchQuery_CreationDateQuery:
|
||||
return creationDateQueryToQuery(q.CreationDateQuery)
|
||||
default:
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "GRPC-Sfefs", "List.Query.Invalid")
|
||||
}
|
||||
@@ -268,12 +293,26 @@ func idsQueryToQuery(q *session.IDsQuery) (query.SearchQuery, error) {
|
||||
return query.NewSessionIDsSearchQuery(q.Ids)
|
||||
}
|
||||
|
||||
func (s *Server) createSessionRequestToCommand(ctx context.Context, req *session.CreateSessionRequest) ([]command.SessionCommand, map[string][]byte, *domain.UserAgent, error) {
|
||||
func creationDateQueryToQuery(q *session.CreationDateQuery) (query.SearchQuery, error) {
|
||||
comparison := timestampComparisons[q.GetMethod()]
|
||||
return query.NewCreationDateQuery(q.GetCreationDate().AsTime(), comparison)
|
||||
}
|
||||
|
||||
func fieldNameToSessionColumn(field session.SessionFieldName) query.Column {
|
||||
switch field {
|
||||
case session.SessionFieldName_SESSION_FIELD_NAME_CREATION_DATE:
|
||||
return query.SessionColumnCreationDate
|
||||
default:
|
||||
return query.Column{}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) createSessionRequestToCommand(ctx context.Context, req *session.CreateSessionRequest) ([]command.SessionCommand, map[string][]byte, *domain.UserAgent, time.Duration, error) {
|
||||
checks, err := s.checksToCommand(ctx, req.Checks)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
return nil, nil, nil, 0, err
|
||||
}
|
||||
return checks, req.GetMetadata(), userAgentToCommand(req.GetUserAgent()), nil
|
||||
return checks, req.GetMetadata(), userAgentToCommand(req.GetUserAgent()), req.GetLifetime().AsDuration(), nil
|
||||
}
|
||||
|
||||
func userAgentToCommand(userAgent *session.UserAgent) *domain.UserAgent {
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc/metadata"
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/types/known/durationpb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
object "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||
@@ -54,7 +55,7 @@ func TestMain(m *testing.M) {
|
||||
}())
|
||||
}
|
||||
|
||||
func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, window time.Duration, metadata map[string][]byte, userAgent *session.UserAgent, factors ...wantFactor) *session.Session {
|
||||
func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, window time.Duration, metadata map[string][]byte, userAgent *session.UserAgent, expirationWindow time.Duration, factors ...wantFactor) *session.Session {
|
||||
t.Helper()
|
||||
require.NotEmpty(t, id)
|
||||
require.NotEmpty(t, token)
|
||||
@@ -75,6 +76,11 @@ func verifyCurrentSession(t testing.TB, id, token string, sequence uint64, windo
|
||||
if !proto.Equal(userAgent, s.GetUserAgent()) {
|
||||
t.Errorf("user agent =\n%v\nwant\n%v", s.GetUserAgent(), userAgent)
|
||||
}
|
||||
if expirationWindow == 0 {
|
||||
assert.Nil(t, s.GetExpirationDate())
|
||||
} else {
|
||||
assert.WithinRange(t, s.GetExpirationDate().AsTime(), time.Now().Add(-expirationWindow), time.Now().Add(expirationWindow))
|
||||
}
|
||||
|
||||
verifyFactors(t, s.GetFactors(), window, factors)
|
||||
return s
|
||||
@@ -137,12 +143,13 @@ func verifyFactors(t testing.TB, factors *session.Factors, window time.Duration,
|
||||
|
||||
func TestServer_CreateSession(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *session.CreateSessionRequest
|
||||
want *session.CreateSessionResponse
|
||||
wantErr bool
|
||||
wantFactors []wantFactor
|
||||
wantUserAgent *session.UserAgent
|
||||
name string
|
||||
req *session.CreateSessionRequest
|
||||
want *session.CreateSessionResponse
|
||||
wantErr bool
|
||||
wantFactors []wantFactor
|
||||
wantUserAgent *session.UserAgent
|
||||
wantExpirationWindow time.Duration
|
||||
}{
|
||||
{
|
||||
name: "empty session",
|
||||
@@ -182,6 +189,27 @@ func TestServer_CreateSession(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "negative lifetime",
|
||||
req: &session.CreateSessionRequest{
|
||||
Metadata: map[string][]byte{"foo": []byte("bar")},
|
||||
Lifetime: durationpb.New(-5 * time.Minute),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "lifetime",
|
||||
req: &session.CreateSessionRequest{
|
||||
Metadata: map[string][]byte{"foo": []byte("bar")},
|
||||
Lifetime: durationpb.New(5 * time.Minute),
|
||||
},
|
||||
want: &session.CreateSessionResponse{
|
||||
Details: &object.Details{
|
||||
ResourceOwner: Tester.Organisation.ID,
|
||||
},
|
||||
},
|
||||
wantExpirationWindow: 5 * time.Minute,
|
||||
},
|
||||
{
|
||||
name: "with user",
|
||||
req: &session.CreateSessionRequest{
|
||||
@@ -253,7 +281,7 @@ func TestServer_CreateSession(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
integration.AssertDetails(t, tt.want, got)
|
||||
|
||||
verifyCurrentSession(t, got.GetSessionId(), got.GetSessionToken(), got.GetDetails().GetSequence(), time.Minute, tt.req.GetMetadata(), tt.wantUserAgent, tt.wantFactors...)
|
||||
verifyCurrentSession(t, got.GetSessionId(), got.GetSessionToken(), got.GetDetails().GetSequence(), time.Minute, tt.req.GetMetadata(), tt.wantUserAgent, tt.wantExpirationWindow, tt.wantFactors...)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -276,7 +304,7 @@ func TestServer_CreateSession_webauthn(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(createResp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true)
|
||||
require.NoError(t, err)
|
||||
@@ -292,7 +320,7 @@ func TestServer_CreateSession_webauthn(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactorUserVerified)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantWebAuthNFactorUserVerified)
|
||||
}
|
||||
|
||||
func TestServer_CreateSession_successfulIntent(t *testing.T) {
|
||||
@@ -308,7 +336,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
|
||||
intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, User.GetUserId(), "id")
|
||||
updateResp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
@@ -322,7 +350,7 @@ func TestServer_CreateSession_successfulIntent(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantIntentFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantIntentFactor)
|
||||
}
|
||||
|
||||
func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) {
|
||||
@@ -338,7 +366,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
|
||||
idpUserID := "id"
|
||||
intentID, token, _, _ := Tester.CreateSuccessfulOAuthIntent(t, idpID, "", idpUserID)
|
||||
@@ -365,7 +393,7 @@ func TestServer_CreateSession_successfulIntentUnknownUserID(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantIntentFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), updateResp.GetSessionToken(), updateResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantIntentFactor)
|
||||
}
|
||||
|
||||
func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) {
|
||||
@@ -381,7 +409,7 @@ func TestServer_CreateSession_startedIntentFalseToken(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), createResp.GetSessionToken(), createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
|
||||
intentID := Tester.CreateIntent(t, idpID)
|
||||
_, err = Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
@@ -433,7 +461,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{})
|
||||
require.NoError(t, err)
|
||||
sessionToken := createResp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, createResp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, createResp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
|
||||
t.Run("check user", func(t *testing.T) {
|
||||
resp, err := Client.SetSession(CTX, &session.SetSessionRequest{
|
||||
@@ -449,7 +477,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor)
|
||||
})
|
||||
|
||||
t.Run("check webauthn, user verified (passkey)", func(t *testing.T) {
|
||||
@@ -464,7 +492,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), true)
|
||||
@@ -481,7 +509,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactorUserVerified)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantWebAuthNFactorUserVerified)
|
||||
})
|
||||
|
||||
userAuthCtx := Tester.WithAuthorizationToken(CTX, sessionToken)
|
||||
@@ -508,7 +536,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
|
||||
assertionData, err := Tester.WebAuthN.CreateAssertionResponse(resp.GetChallenges().GetWebAuthN().GetPublicKeyCredentialRequestOptions(), false)
|
||||
@@ -525,7 +553,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantWebAuthNFactor)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -544,7 +572,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantTOTPFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantWebAuthNFactor, wantTOTPFactor)
|
||||
})
|
||||
|
||||
t.Run("check OTP SMS", func(t *testing.T) {
|
||||
@@ -556,7 +584,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
|
||||
otp := resp.GetChallenges().GetOtpSms()
|
||||
@@ -573,7 +601,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantOTPSMSFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantWebAuthNFactor, wantOTPSMSFactor)
|
||||
})
|
||||
|
||||
t.Run("check OTP Email", func(t *testing.T) {
|
||||
@@ -587,7 +615,7 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), resp.GetSessionToken(), resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
|
||||
otp := resp.GetChallenges().GetOtpEmail()
|
||||
@@ -604,10 +632,34 @@ func TestServer_SetSession_flow(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
sessionToken = resp.GetSessionToken()
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, wantUserFactor, wantWebAuthNFactor, wantOTPEmailFactor)
|
||||
verifyCurrentSession(t, createResp.GetSessionId(), sessionToken, resp.GetDetails().GetSequence(), time.Minute, nil, nil, 0, wantUserFactor, wantWebAuthNFactor, wantOTPEmailFactor)
|
||||
})
|
||||
}
|
||||
|
||||
func TestServer_SetSession_expired(t *testing.T) {
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{
|
||||
Lifetime: durationpb.New(20 * time.Second),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// test session token works
|
||||
sessionResp, err := Tester.Client.SessionV2.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: createResp.GetSessionToken(),
|
||||
Lifetime: durationpb.New(20 * time.Second),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure session expires and does not work anymore
|
||||
time.Sleep(20 * time.Second)
|
||||
_, err = Tester.Client.SessionV2.SetSession(CTX, &session.SetSessionRequest{
|
||||
SessionId: createResp.GetSessionId(),
|
||||
SessionToken: sessionResp.GetSessionToken(),
|
||||
Lifetime: durationpb.New(20 * time.Second),
|
||||
})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_ZITADEL_API_missing_authentication(t *testing.T) {
|
||||
// create new, empty session
|
||||
createResp, err := Client.CreateSession(CTX, &session.CreateSessionRequest{})
|
||||
@@ -629,7 +681,7 @@ func Test_ZITADEL_API_missing_mfa(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ZITADEL_API_success(t *testing.T) {
|
||||
id, token, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, User.GetUserId())
|
||||
id, token, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, User.GetUserId())
|
||||
|
||||
ctx := Tester.WithAuthorizationToken(context.Background(), token)
|
||||
sessionResp, err := Tester.Client.SessionV2.GetSession(ctx, &session.GetSessionRequest{SessionId: id})
|
||||
@@ -641,7 +693,7 @@ func Test_ZITADEL_API_success(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ZITADEL_API_session_not_found(t *testing.T) {
|
||||
id, token, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, User.GetUserId())
|
||||
id, token, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, User.GetUserId())
|
||||
|
||||
// test session token works
|
||||
ctx := Tester.WithAuthorizationToken(context.Background(), token)
|
||||
@@ -658,3 +710,18 @@ func Test_ZITADEL_API_session_not_found(t *testing.T) {
|
||||
_, err = Tester.Client.SessionV2.GetSession(ctx, &session.GetSessionRequest{SessionId: id})
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_ZITADEL_API_session_expired(t *testing.T) {
|
||||
id, token, _, _ := Tester.CreateVerifiedWebAuthNSessionWithLifetime(t, CTX, User.GetUserId(), 20*time.Second)
|
||||
|
||||
// test session token works
|
||||
ctx := Tester.WithAuthorizationToken(context.Background(), token)
|
||||
_, err := Tester.Client.SessionV2.GetSession(ctx, &session.GetSessionRequest{SessionId: id})
|
||||
require.NoError(t, err)
|
||||
|
||||
// ensure session expires and does not work anymore
|
||||
time.Sleep(20 * time.Second)
|
||||
sessionResp, err := Tester.Client.SessionV2.GetSession(ctx, &session.GetSessionRequest{SessionId: id})
|
||||
require.Error(t, err)
|
||||
require.Nil(t, sessionResp)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
objpb "github.com/zitadel/zitadel/pkg/grpc/object"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
caos_errs "github.com/zitadel/zitadel/internal/errors"
|
||||
@@ -22,12 +23,16 @@ import (
|
||||
session "github.com/zitadel/zitadel/pkg/grpc/session/v2beta"
|
||||
)
|
||||
|
||||
var (
|
||||
creationDate = time.Date(2023, 10, 10, 14, 15, 0, 0, time.UTC)
|
||||
)
|
||||
|
||||
func Test_sessionsToPb(t *testing.T) {
|
||||
now := time.Now()
|
||||
past := now.Add(-time.Hour)
|
||||
|
||||
sessions := []*query.Session{
|
||||
{ // no factor, with user agent
|
||||
{ // no factor, with user agent and expiration
|
||||
ID: "999",
|
||||
CreationDate: now,
|
||||
ChangeDate: now,
|
||||
@@ -42,6 +47,7 @@ func Test_sessionsToPb(t *testing.T) {
|
||||
IP: net.IPv4(1, 2, 3, 4),
|
||||
Header: http.Header{"foo": []string{"foo", "bar"}},
|
||||
},
|
||||
Expiration: now,
|
||||
},
|
||||
{ // user factor
|
||||
ID: "999",
|
||||
@@ -124,7 +130,7 @@ func Test_sessionsToPb(t *testing.T) {
|
||||
}
|
||||
|
||||
want := []*session.Session{
|
||||
{ // no factor, with user agent
|
||||
{ // no factor, with user agent and expiration
|
||||
Id: "999",
|
||||
CreationDate: timestamppb.New(now),
|
||||
ChangeDate: timestamppb.New(now),
|
||||
@@ -139,6 +145,7 @@ func Test_sessionsToPb(t *testing.T) {
|
||||
"foo": {Values: []string{"foo", "bar"}},
|
||||
},
|
||||
},
|
||||
ExpirationDate: timestamppb.New(now),
|
||||
},
|
||||
{ // user factor
|
||||
Id: "999",
|
||||
@@ -307,11 +314,18 @@ func mustNewListQuery(t testing.TB, column query.Column, list []any, compare que
|
||||
return q
|
||||
}
|
||||
|
||||
func mustNewTimestampQuery(t testing.TB, column query.Column, ts time.Time, compare query.TimestampComparison) query.SearchQuery {
|
||||
q, err := query.NewTimestampQuery(column, ts, compare)
|
||||
require.NoError(t, err)
|
||||
return q
|
||||
}
|
||||
|
||||
func Test_listSessionsRequestToQuery(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
req *session.ListSessionsRequest
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
@@ -335,6 +349,26 @@ func Test_listSessionsRequestToQuery(t *testing.T) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "default request with sorting column",
|
||||
args: args{
|
||||
ctx: authz.NewMockContext("123", "456", "789"),
|
||||
req: &session.ListSessionsRequest{
|
||||
SortingColumn: session.SessionFieldName_SESSION_FIELD_NAME_CREATION_DATE,
|
||||
},
|
||||
},
|
||||
want: &query.SessionsSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: 0,
|
||||
Limit: 0,
|
||||
SortingColumn: query.SessionColumnCreationDate,
|
||||
Asc: false,
|
||||
},
|
||||
Queries: []query.SearchQuery{
|
||||
mustNewTextQuery(t, query.SessionColumnCreator, "789", query.TextEquals),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with list query and sessions",
|
||||
args: args{
|
||||
@@ -356,6 +390,17 @@ func Test_listSessionsRequestToQuery(t *testing.T) {
|
||||
Ids: []string{"4", "5", "6"},
|
||||
},
|
||||
}},
|
||||
{Query: &session.SearchQuery_UserIdQuery{
|
||||
UserIdQuery: &session.UserIDQuery{
|
||||
Id: "10",
|
||||
},
|
||||
}},
|
||||
{Query: &session.SearchQuery_CreationDateQuery{
|
||||
CreationDateQuery: &session.CreationDateQuery{
|
||||
CreationDate: timestamppb.New(creationDate),
|
||||
Method: objpb.TimestampQueryMethod_TIMESTAMP_QUERY_METHOD_GREATER,
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -368,6 +413,8 @@ func Test_listSessionsRequestToQuery(t *testing.T) {
|
||||
Queries: []query.SearchQuery{
|
||||
mustNewListQuery(t, query.SessionColumnID, []interface{}{"1", "2", "3"}, query.ListIn),
|
||||
mustNewListQuery(t, query.SessionColumnID, []interface{}{"4", "5", "6"}, query.ListIn),
|
||||
mustNewTextQuery(t, query.SessionColumnUserID, "10", query.TextEquals),
|
||||
mustNewTimestampQuery(t, query.SessionColumnCreationDate, creationDate, query.TimestampGreater),
|
||||
mustNewTextQuery(t, query.SessionColumnCreator, "789", query.TextEquals),
|
||||
},
|
||||
},
|
||||
@@ -485,7 +532,7 @@ func Test_sessionQueryToQuery(t *testing.T) {
|
||||
wantErr: caos_errs.ThrowInvalidArgument(nil, "GRPC-Sfefs", "List.Query.Invalid"),
|
||||
},
|
||||
{
|
||||
name: "query",
|
||||
name: "ids query",
|
||||
args: args{&session.SearchQuery{
|
||||
Query: &session.SearchQuery_IdsQuery{
|
||||
IdsQuery: &session.IDsQuery{
|
||||
@@ -495,6 +542,40 @@ func Test_sessionQueryToQuery(t *testing.T) {
|
||||
}},
|
||||
want: mustNewListQuery(t, query.SessionColumnID, []interface{}{"1", "2", "3"}, query.ListIn),
|
||||
},
|
||||
{
|
||||
name: "user id query",
|
||||
args: args{&session.SearchQuery{
|
||||
Query: &session.SearchQuery_UserIdQuery{
|
||||
UserIdQuery: &session.UserIDQuery{
|
||||
Id: "10",
|
||||
},
|
||||
},
|
||||
}},
|
||||
want: mustNewTextQuery(t, query.SessionColumnUserID, "10", query.TextEquals),
|
||||
},
|
||||
{
|
||||
name: "creation date query",
|
||||
args: args{&session.SearchQuery{
|
||||
Query: &session.SearchQuery_CreationDateQuery{
|
||||
CreationDateQuery: &session.CreationDateQuery{
|
||||
CreationDate: timestamppb.New(creationDate),
|
||||
Method: objpb.TimestampQueryMethod_TIMESTAMP_QUERY_METHOD_LESS,
|
||||
},
|
||||
},
|
||||
}},
|
||||
want: mustNewTimestampQuery(t, query.SessionColumnCreationDate, creationDate, query.TimestampLess),
|
||||
},
|
||||
{
|
||||
name: "creation date query with default method",
|
||||
args: args{&session.SearchQuery{
|
||||
Query: &session.SearchQuery_CreationDateQuery{
|
||||
CreationDateQuery: &session.CreationDateQuery{
|
||||
CreationDate: timestamppb.New(creationDate),
|
||||
},
|
||||
},
|
||||
}},
|
||||
want: mustNewTimestampQuery(t, query.SessionColumnCreationDate, creationDate, query.TimestampEquals),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
||||
@@ -7,10 +7,10 @@ import (
|
||||
user_pb "github.com/zitadel/zitadel/pkg/grpc/user"
|
||||
)
|
||||
|
||||
func UserQueriesToQuery(queries []*user_pb.SearchQuery) (_ []query.SearchQuery, err error) {
|
||||
func UserQueriesToQuery(queries []*user_pb.SearchQuery, level uint8) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = UserQueryToQuery(query)
|
||||
q[i], err = UserQueryToQuery(query, level)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -18,7 +18,11 @@ func UserQueriesToQuery(queries []*user_pb.SearchQuery) (_ []query.SearchQuery,
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func UserQueryToQuery(query *user_pb.SearchQuery) (query.SearchQuery, error) {
|
||||
func UserQueryToQuery(query *user_pb.SearchQuery, level uint8) (query.SearchQuery, error) {
|
||||
if level > 20 {
|
||||
// can't go deeper than 20 levels of nesting.
|
||||
return nil, errors.ThrowInvalidArgument(nil, "USER-zsQ97", "Errors.User.TooManyNestingLevels")
|
||||
}
|
||||
switch q := query.Query.(type) {
|
||||
case *user_pb.SearchQuery_UserNameQuery:
|
||||
return UserNameQueryToQuery(q.UserNameQuery)
|
||||
@@ -42,6 +46,12 @@ func UserQueryToQuery(query *user_pb.SearchQuery) (query.SearchQuery, error) {
|
||||
return ResourceOwnerQueryToQuery(q.ResourceOwner)
|
||||
case *user_pb.SearchQuery_InUserIdsQuery:
|
||||
return InUserIdsQueryToQuery(q.InUserIdsQuery)
|
||||
case *user_pb.SearchQuery_OrQuery:
|
||||
return OrQueryToQuery(q.OrQuery, level)
|
||||
case *user_pb.SearchQuery_AndQuery:
|
||||
return AndQueryToQuery(q.AndQuery, level)
|
||||
case *user_pb.SearchQuery_NotQuery:
|
||||
return NotQueryToQuery(q.NotQuery, level)
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgument(nil, "GRPC-vR9nC", "List.Query.Invalid")
|
||||
}
|
||||
@@ -90,3 +100,24 @@ func ResourceOwnerQueryToQuery(q *user_pb.ResourceOwnerQuery) (query.SearchQuery
|
||||
func InUserIdsQueryToQuery(q *user_pb.InUserIDQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserInUserIdsSearchQuery(q.UserIds)
|
||||
}
|
||||
func OrQueryToQuery(q *user_pb.OrQuery, level uint8) (query.SearchQuery, error) {
|
||||
mappedQueries, err := UserQueriesToQuery(q.Queries, level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserOrSearchQuery(mappedQueries)
|
||||
}
|
||||
func AndQueryToQuery(q *user_pb.AndQuery, level uint8) (query.SearchQuery, error) {
|
||||
mappedQueries, err := UserQueriesToQuery(q.Queries, level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserAndSearchQuery(mappedQueries)
|
||||
}
|
||||
func NotQueryToQuery(q *user_pb.NotQuery, level uint8) (query.SearchQuery, error) {
|
||||
mappedQuery, err := UserQueryToQuery(q.Query, level+1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return query.NewUserNotSearchQuery(mappedQuery)
|
||||
}
|
||||
|
||||
@@ -16,13 +16,13 @@ import (
|
||||
func TestServer_AddOTPSMS(t *testing.T) {
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
// TODO: add when phone can be added to user
|
||||
/*
|
||||
userIDPhone := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userIDPhone)
|
||||
_, sessionTokenPhone, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userIDPhone)
|
||||
_, sessionTokenPhone, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, userIDPhone)
|
||||
*/
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -99,7 +99,7 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
|
||||
/*
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
*/
|
||||
|
||||
type args struct {
|
||||
@@ -157,7 +157,7 @@ func TestServer_RemoveOTPSMS(t *testing.T) {
|
||||
func TestServer_AddOTPEmail(t *testing.T) {
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
userVerified := Tester.CreateHumanUser(CTX)
|
||||
_, err := Tester.Client.UserV2.VerifyEmail(CTX, &user.VerifyEmailRequest{
|
||||
@@ -166,7 +166,7 @@ func TestServer_AddOTPEmail(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
Tester.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
@@ -238,11 +238,11 @@ func TestServer_AddOTPEmail(t *testing.T) {
|
||||
func TestServer_RemoveOTPEmail(t *testing.T) {
|
||||
userID := Tester.CreateHumanUser(CTX).GetUserId()
|
||||
Tester.RegisterUserPasskey(CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userID)
|
||||
_, sessionToken, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, userID)
|
||||
|
||||
userVerified := Tester.CreateHumanUser(CTX)
|
||||
Tester.RegisterUserPasskey(CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreateVerfiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
_, sessionTokenVerified, _, _ := Tester.CreateVerifiedWebAuthNSession(t, CTX, userVerified.GetUserId())
|
||||
userVerifiedCtx := Tester.WithAuthorizationToken(context.Background(), sessionTokenVerified)
|
||||
_, err := Tester.Client.UserV2.VerifyEmail(userVerifiedCtx, &user.VerifyEmailRequest{
|
||||
UserId: userVerified.GetUserId(),
|
||||
|
||||
@@ -36,7 +36,7 @@ func TestOPStorage_CreateAuthRequest(t *testing.T) {
|
||||
func TestOPStorage_CreateAccessToken_code(t *testing.T) {
|
||||
clientID := createClient(t)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -75,7 +75,7 @@ func TestOPStorage_CreateAccessToken_code(t *testing.T) {
|
||||
func TestOPStorage_CreateAccessToken_implicit(t *testing.T) {
|
||||
clientID := createImplicitClient(t)
|
||||
authRequestID := createAuthRequestImplicit(t, clientID, redirectURIImplicit)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -125,7 +125,7 @@ func TestOPStorage_CreateAccessToken_implicit(t *testing.T) {
|
||||
func TestOPStorage_CreateAccessAndRefreshTokens_code(t *testing.T) {
|
||||
clientID := createClient(t)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -150,7 +150,7 @@ func TestOPStorage_CreateAccessAndRefreshTokens_refresh(t *testing.T) {
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -186,7 +186,7 @@ func TestOPStorage_RevokeToken_access_token(t *testing.T) {
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -229,7 +229,7 @@ func TestOPStorage_RevokeToken_access_token_invalid_token_hint_type(t *testing.T
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -266,7 +266,7 @@ func TestOPStorage_RevokeToken_refresh_token(t *testing.T) {
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -309,7 +309,7 @@ func TestOPStorage_RevokeToken_refresh_token_invalid_token_type_hint(t *testing.
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -344,7 +344,7 @@ func TestOPStorage_RevokeToken_refresh_token_invalid_token_type_hint(t *testing.
|
||||
func TestOPStorage_RevokeToken_invalid_client(t *testing.T) {
|
||||
clientID := createClient(t)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -376,7 +376,7 @@ func TestOPStorage_TerminateSession(t *testing.T) {
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -413,7 +413,7 @@ func TestOPStorage_TerminateSession_refresh_grant(t *testing.T) {
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -457,7 +457,7 @@ func TestOPStorage_TerminateSession_empty_id_token_hint(t *testing.T) {
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
func TestOPStorage_SetUserinfoFromToken(t *testing.T) {
|
||||
clientID := createClient(t)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -67,7 +67,7 @@ func TestOPStorage_SetIntrospectionFromToken(t *testing.T) {
|
||||
|
||||
scope := []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopeOfflineAccess}
|
||||
authRequestID := createAuthRequest(t, app.GetClientId(), redirectURI, scope...)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
|
||||
@@ -57,7 +57,7 @@ func TestMain(m *testing.M) {
|
||||
func Test_ZITADEL_API_missing_audience_scope(t *testing.T) {
|
||||
clientID := createClient(t)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -148,7 +148,7 @@ func Test_ZITADEL_API_missing_mfa(t *testing.T) {
|
||||
func Test_ZITADEL_API_success(t *testing.T) {
|
||||
clientID := createClient(t)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, zitadelAudienceScope)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -177,7 +177,7 @@ func Test_ZITADEL_API_success(t *testing.T) {
|
||||
func Test_ZITADEL_API_inactive_access_token(t *testing.T) {
|
||||
clientID := createClient(t)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
@@ -219,7 +219,7 @@ func Test_ZITADEL_API_terminated_session(t *testing.T) {
|
||||
provider, err := Tester.CreateRelyingParty(CTX, clientID, redirectURI)
|
||||
require.NoError(t, err)
|
||||
authRequestID := createAuthRequest(t, clientID, redirectURI, oidc.ScopeOpenID, oidc.ScopeOfflineAccess, zitadelAudienceScope)
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerfiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
sessionID, sessionToken, startTime, changeTime := Tester.CreateVerifiedWebAuthNSession(t, CTXLOGIN, User.GetUserId())
|
||||
linkResp, err := Tester.Client.OIDCv2.CreateCallback(CTXLOGIN, &oidc_pb.CreateCallbackRequest{
|
||||
AuthRequestId: authRequestID,
|
||||
CallbackKind: &oidc_pb.CreateCallbackRequest_Session{
|
||||
|
||||
@@ -109,10 +109,10 @@ func NewServer(
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "OIDC-D3gq1", "cannot create options: %w")
|
||||
}
|
||||
provider, err := op.NewForwardedOpenIDProvider(
|
||||
"",
|
||||
provider, err := op.NewProvider(
|
||||
opConfig,
|
||||
storage,
|
||||
op.IssuerFromForwardedOrHost("", op.WithIssuerFromCustomHeaders("forwarded", "x-zitadel-forwarded")),
|
||||
options...,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -120,11 +120,12 @@ func NewServer(
|
||||
}
|
||||
|
||||
server := &Server{
|
||||
storage: storage,
|
||||
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
|
||||
query: query,
|
||||
fallbackLogger: fallbackLogger,
|
||||
hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant.
|
||||
storage: storage,
|
||||
LegacyServer: op.NewLegacyServer(provider, endpoints(config.CustomEndpoints)),
|
||||
query: query,
|
||||
fallbackLogger: fallbackLogger,
|
||||
hashAlg: crypto.NewBCrypt(10), // as we are only verifying in oidc, the cost is already part of the hash string and the config here is irrelevant.
|
||||
signingKeyAlgorithm: config.SigningKeyAlgorithm,
|
||||
}
|
||||
metricTypes := []metrics.MetricType{metrics.MetricTypeRequestCount, metrics.MetricTypeStatusCode, metrics.MetricTypeTotalCount}
|
||||
server.Handler = op.RegisterLegacyServer(server, op.WithHTTPMiddleware(
|
||||
|
||||
@@ -23,8 +23,9 @@ type Server struct {
|
||||
query *query.Queries
|
||||
command *command.Commands
|
||||
|
||||
fallbackLogger *slog.Logger
|
||||
hashAlg crypto.HashAlgorithm
|
||||
fallbackLogger *slog.Logger
|
||||
hashAlg crypto.HashAlgorithm
|
||||
signingKeyAlgorithm string
|
||||
}
|
||||
|
||||
func endpoints(endpointConfig *EndpointConfig) op.Endpoints {
|
||||
@@ -99,7 +100,7 @@ func (s *Server) Discovery(ctx context.Context, r *op.Request[struct{}]) (_ *op.
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
return s.LegacyServer.Discovery(ctx, r)
|
||||
return op.NewResponse(s.createDiscoveryConfig(ctx)), nil
|
||||
}
|
||||
|
||||
func (s *Server) Keys(ctx context.Context, r *op.Request[struct{}]) (_ *op.Response, err error) {
|
||||
@@ -199,3 +200,34 @@ func (s *Server) EndSession(ctx context.Context, r *op.Request[oidc.EndSessionRe
|
||||
|
||||
return s.LegacyServer.EndSession(ctx, r)
|
||||
}
|
||||
|
||||
func (s *Server) createDiscoveryConfig(ctx context.Context) *oidc.DiscoveryConfiguration {
|
||||
issuer := op.IssuerFromContext(ctx)
|
||||
return &oidc.DiscoveryConfiguration{
|
||||
Issuer: issuer,
|
||||
AuthorizationEndpoint: s.Endpoints().Authorization.Absolute(issuer),
|
||||
TokenEndpoint: s.Endpoints().Token.Absolute(issuer),
|
||||
IntrospectionEndpoint: s.Endpoints().Introspection.Absolute(issuer),
|
||||
UserinfoEndpoint: s.Endpoints().Userinfo.Absolute(issuer),
|
||||
RevocationEndpoint: s.Endpoints().Revocation.Absolute(issuer),
|
||||
EndSessionEndpoint: s.Endpoints().EndSession.Absolute(issuer),
|
||||
JwksURI: s.Endpoints().JwksURI.Absolute(issuer),
|
||||
DeviceAuthorizationEndpoint: s.Endpoints().DeviceAuthorization.Absolute(issuer),
|
||||
ScopesSupported: op.Scopes(s.Provider()),
|
||||
ResponseTypesSupported: op.ResponseTypes(s.Provider()),
|
||||
GrantTypesSupported: op.GrantTypes(s.Provider()),
|
||||
SubjectTypesSupported: op.SubjectTypes(s.Provider()),
|
||||
IDTokenSigningAlgValuesSupported: []string{s.signingKeyAlgorithm},
|
||||
RequestObjectSigningAlgValuesSupported: op.RequestObjectSigAlgorithms(s.Provider()),
|
||||
TokenEndpointAuthMethodsSupported: op.AuthMethodsTokenEndpoint(s.Provider()),
|
||||
TokenEndpointAuthSigningAlgValuesSupported: op.TokenSigAlgorithms(s.Provider()),
|
||||
IntrospectionEndpointAuthSigningAlgValuesSupported: op.IntrospectionSigAlgorithms(s.Provider()),
|
||||
IntrospectionEndpointAuthMethodsSupported: op.AuthMethodsIntrospectionEndpoint(s.Provider()),
|
||||
RevocationEndpointAuthSigningAlgValuesSupported: op.RevocationSigAlgorithms(s.Provider()),
|
||||
RevocationEndpointAuthMethodsSupported: op.AuthMethodsRevocationEndpoint(s.Provider()),
|
||||
ClaimsSupported: op.SupportedClaims(s.Provider()),
|
||||
CodeChallengeMethodsSupported: op.CodeChallengeMethods(s.Provider()),
|
||||
UILocalesSupported: s.Provider().SupportedUILocales(),
|
||||
RequestParameterSupported: s.Provider().RequestObjectSupported(),
|
||||
}
|
||||
}
|
||||
|
||||
119
internal/api/oidc/server_test.go
Normal file
119
internal/api/oidc/server_test.go
Normal file
@@ -0,0 +1,119 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/zitadel/oidc/v3/pkg/oidc"
|
||||
"github.com/zitadel/oidc/v3/pkg/op"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
func TestServer_createDiscoveryConfig(t *testing.T) {
|
||||
type fields struct {
|
||||
LegacyServer *op.LegacyServer
|
||||
signingKeyAlgorithm string
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *oidc.DiscoveryConfiguration
|
||||
}{
|
||||
{
|
||||
"config",
|
||||
fields{
|
||||
LegacyServer: op.NewLegacyServer(
|
||||
func() *op.Provider {
|
||||
provider, _ := op.NewForwardedOpenIDProvider("path",
|
||||
&op.Config{
|
||||
CodeMethodS256: true,
|
||||
AuthMethodPost: true,
|
||||
AuthMethodPrivateKeyJWT: true,
|
||||
GrantTypeRefreshToken: true,
|
||||
RequestObjectSupported: true,
|
||||
SupportedUILocales: []language.Tag{language.English, language.German},
|
||||
},
|
||||
nil,
|
||||
)
|
||||
return provider
|
||||
}(),
|
||||
op.Endpoints{
|
||||
Authorization: op.NewEndpoint("auth"),
|
||||
Token: op.NewEndpoint("token"),
|
||||
Introspection: op.NewEndpoint("introspect"),
|
||||
Userinfo: op.NewEndpoint("userinfo"),
|
||||
Revocation: op.NewEndpoint("revoke"),
|
||||
EndSession: op.NewEndpoint("logout"),
|
||||
JwksURI: op.NewEndpoint("keys"),
|
||||
DeviceAuthorization: op.NewEndpoint("device"),
|
||||
},
|
||||
),
|
||||
signingKeyAlgorithm: "RS256",
|
||||
},
|
||||
args{
|
||||
ctx: op.ContextWithIssuer(context.Background(), "https://issuer.com"),
|
||||
},
|
||||
&oidc.DiscoveryConfiguration{
|
||||
Issuer: "https://issuer.com",
|
||||
AuthorizationEndpoint: "https://issuer.com/auth",
|
||||
TokenEndpoint: "https://issuer.com/token",
|
||||
IntrospectionEndpoint: "https://issuer.com/introspect",
|
||||
UserinfoEndpoint: "https://issuer.com/userinfo",
|
||||
RevocationEndpoint: "https://issuer.com/revoke",
|
||||
EndSessionEndpoint: "https://issuer.com/logout",
|
||||
DeviceAuthorizationEndpoint: "https://issuer.com/device",
|
||||
CheckSessionIframe: "",
|
||||
JwksURI: "https://issuer.com/keys",
|
||||
RegistrationEndpoint: "",
|
||||
ScopesSupported: []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail, oidc.ScopePhone, oidc.ScopeAddress, oidc.ScopeOfflineAccess},
|
||||
ResponseTypesSupported: []string{string(oidc.ResponseTypeCode), string(oidc.ResponseTypeIDTokenOnly), string(oidc.ResponseTypeIDToken)},
|
||||
ResponseModesSupported: nil,
|
||||
GrantTypesSupported: []oidc.GrantType{oidc.GrantTypeCode, oidc.GrantTypeImplicit, oidc.GrantTypeRefreshToken, oidc.GrantTypeBearer},
|
||||
ACRValuesSupported: nil,
|
||||
SubjectTypesSupported: []string{"public"},
|
||||
IDTokenSigningAlgValuesSupported: []string{"RS256"},
|
||||
IDTokenEncryptionAlgValuesSupported: nil,
|
||||
IDTokenEncryptionEncValuesSupported: nil,
|
||||
UserinfoSigningAlgValuesSupported: nil,
|
||||
UserinfoEncryptionAlgValuesSupported: nil,
|
||||
UserinfoEncryptionEncValuesSupported: nil,
|
||||
RequestObjectSigningAlgValuesSupported: []string{"RS256"},
|
||||
RequestObjectEncryptionAlgValuesSupported: nil,
|
||||
RequestObjectEncryptionEncValuesSupported: nil,
|
||||
TokenEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost, oidc.AuthMethodPrivateKeyJWT},
|
||||
TokenEndpointAuthSigningAlgValuesSupported: []string{"RS256"},
|
||||
RevocationEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodNone, oidc.AuthMethodBasic, oidc.AuthMethodPost, oidc.AuthMethodPrivateKeyJWT},
|
||||
RevocationEndpointAuthSigningAlgValuesSupported: []string{"RS256"},
|
||||
IntrospectionEndpointAuthMethodsSupported: []oidc.AuthMethod{oidc.AuthMethodBasic, oidc.AuthMethodPrivateKeyJWT},
|
||||
IntrospectionEndpointAuthSigningAlgValuesSupported: []string{"RS256"},
|
||||
DisplayValuesSupported: nil,
|
||||
ClaimTypesSupported: nil,
|
||||
ClaimsSupported: []string{"sub", "aud", "exp", "iat", "iss", "auth_time", "nonce", "acr", "amr", "c_hash", "at_hash", "act", "scopes", "client_id", "azp", "preferred_username", "name", "family_name", "given_name", "locale", "email", "email_verified", "phone_number", "phone_number_verified"},
|
||||
ClaimsParameterSupported: false,
|
||||
CodeChallengeMethodsSupported: []oidc.CodeChallengeMethod{"S256"},
|
||||
ServiceDocumentation: "",
|
||||
ClaimsLocalesSupported: nil,
|
||||
UILocalesSupported: []language.Tag{language.English, language.German},
|
||||
RequestParameterSupported: true,
|
||||
RequestURIParameterSupported: false,
|
||||
RequireRequestURIRegistration: false,
|
||||
OPPolicyURI: "",
|
||||
OPTermsOfServiceURI: "",
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
s := &Server{
|
||||
LegacyServer: tt.fields.LegacyServer,
|
||||
signingKeyAlgorithm: tt.fields.signingKeyAlgorithm,
|
||||
}
|
||||
assert.Equalf(t, tt.want, s.createDiscoveryConfig(tt.args.ctx), "createDiscoveryConfig(%v)", tt.args.ctx)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1173,8 +1173,9 @@ func mapExternalNotFoundOptionFormDataToLoginUser(formData *externalNotFoundOpti
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Login) sessionParamsFromAuthRequest(ctx context.Context, authReq *domain.AuthRequest, identityProviderID string) []any {
|
||||
params := []any{authReq.AgentID}
|
||||
func (l *Login) sessionParamsFromAuthRequest(ctx context.Context, authReq *domain.AuthRequest, identityProviderID string) []idp.Parameter {
|
||||
params := make([]idp.Parameter, 1, 2)
|
||||
params[0] = idp.UserAgentID(authReq.AgentID)
|
||||
|
||||
if authReq.UserID != "" && identityProviderID != "" {
|
||||
links, err := l.getUserLinks(ctx, authReq.UserID, identityProviderID)
|
||||
@@ -1183,27 +1184,21 @@ func (l *Login) sessionParamsFromAuthRequest(ctx context.Context, authReq *domai
|
||||
return params
|
||||
}
|
||||
if len(links.Links) == 1 {
|
||||
return append(params, keyAndValueToAuthURLOpt("login_hint", links.Links[0].ProvidedUsername))
|
||||
return append(params, idp.LoginHintParam(links.Links[0].ProvidedUsername))
|
||||
}
|
||||
}
|
||||
if authReq.UserName != "" {
|
||||
return append(params, keyAndValueToAuthURLOpt("login_hint", authReq.UserName))
|
||||
return append(params, idp.LoginHintParam(authReq.UserName))
|
||||
}
|
||||
if authReq.LoginName != "" {
|
||||
return append(params, keyAndValueToAuthURLOpt("login_hint", authReq.LoginName))
|
||||
return append(params, idp.LoginHintParam(authReq.LoginName))
|
||||
}
|
||||
if authReq.LoginHint != "" {
|
||||
return append(params, keyAndValueToAuthURLOpt("login_hint", authReq.LoginHint))
|
||||
return append(params, idp.LoginHintParam(authReq.LoginHint))
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
func keyAndValueToAuthURLOpt(key, value string) rp.AuthURLOpt {
|
||||
return func() []oauth2.AuthCodeOption {
|
||||
return []oauth2.AuthCodeOption{oauth2.SetAuthURLParam(key, value)}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Login) getUserLinks(ctx context.Context, userID, idpID string) (*query.IDPUserLinks, error) {
|
||||
userIDQuery, err := query.NewIDPUserLinksUserIDSearchQuery(userID)
|
||||
if err != nil {
|
||||
|
||||
@@ -242,6 +242,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Пол
|
||||
Female: Женски пол
|
||||
Male: Мъжки
|
||||
@@ -277,6 +279,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Правила и условия
|
||||
TosConfirm: Приемам
|
||||
TosLinkText: TOS
|
||||
@@ -336,7 +340,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
DeviceAuth:
|
||||
Title: Упълномощаване на устройството
|
||||
UserCode:
|
||||
|
||||
487
internal/api/ui/login/static/i18n/cs.yaml
Normal file
487
internal/api/ui/login/static/i18n/cs.yaml
Normal file
@@ -0,0 +1,487 @@
|
||||
Login:
|
||||
Title: Vítejte zpět!
|
||||
Description: Zadejte své přihlašovací údaje.
|
||||
TitleLinking: Přihlášení pro propojení uživatele
|
||||
DescriptionLinking: Zadejte své přihlašovací údaje pro propojení vašeho externího účtu.
|
||||
LoginNameLabel: Přihlašovací jméno
|
||||
UsernamePlaceHolder: uživatelské jméno
|
||||
LoginnamePlaceHolder: uzivatelskejmeno@doména
|
||||
ExternalUserDescription: Přihlášení s externím účtem.
|
||||
MustBeMemberOfOrg: Uživatel musí být členem organizace {{.OrgName}}.
|
||||
RegisterButtonText: Registrovat
|
||||
NextButtonText: Další
|
||||
|
||||
LDAP:
|
||||
Title: Přihlášení
|
||||
Description: Zadejte své přihlašovací údaje.
|
||||
LoginNameLabel: Přihlašovací jméno
|
||||
PasswordLabel: Heslo
|
||||
NextButtonText: Další
|
||||
|
||||
SelectAccount:
|
||||
Title: Vyberte účet
|
||||
Description: Použijte svůj účet
|
||||
TitleLinking: Vyberte účet pro propojení uživatele
|
||||
DescriptionLinking: Vyberte účet pro propojení s vaším externím uživatelem.
|
||||
OtherUser: Jiný uživatel
|
||||
SessionState0: aktivní
|
||||
SessionState1: Odhlášen
|
||||
MustBeMemberOfOrg: Uživatel musí být členem organizace {{.OrgName}}.
|
||||
|
||||
Password:
|
||||
Title: Heslo
|
||||
Description: Zadejte své přihlašovací údaje.
|
||||
PasswordLabel: Heslo
|
||||
MinLength: Minimální délka
|
||||
HasUppercase: Velké písmeno
|
||||
HasLowercase: Malé písmeno
|
||||
HasNumber: Číslo
|
||||
HasSymbol: Symbol
|
||||
Confirmation: Potvrzení shody
|
||||
ResetLinkText: Obnovit heslo
|
||||
BackButtonText: Zpět
|
||||
NextButtonText: Další
|
||||
|
||||
UsernameChange:
|
||||
Title: Změna uživatelského jména
|
||||
Description: Nastavte své nové uživatelské jméno
|
||||
UsernameLabel: Uživatelské jméno
|
||||
CancelButtonText: Zrušit
|
||||
NextButtonText: Další
|
||||
|
||||
UsernameChangeDone:
|
||||
Title: Uživatelské jméno bylo změněno
|
||||
Description: Vaše uživatelské jméno bylo úspěšně změněno.
|
||||
NextButtonText: Další
|
||||
|
||||
InitPassword:
|
||||
Title: Nastavit heslo
|
||||
Description: Obdrželi jste kód, který musíte vložit níže, abyste nastavili své nové heslo.
|
||||
CodeLabel: Kód
|
||||
NewPasswordLabel: Nové heslo
|
||||
NewPasswordConfirmLabel: Potvrzení hesla
|
||||
ResendButtonText: Znovu poslat kód
|
||||
NextButtonText: Další
|
||||
|
||||
InitPasswordDone:
|
||||
Title: Heslo nastaveno
|
||||
Description: Heslo bylo úspěšně nastaveno
|
||||
NextButtonText: Další
|
||||
CancelButtonText: Zrušit
|
||||
|
||||
InitUser:
|
||||
Title: Aktivovat uživatele
|
||||
Description: Ověřte váš e-mail pomocí níže uvedeného kódu a nastavte své heslo.
|
||||
CodeLabel: Kód
|
||||
NewPasswordLabel: Nové heslo
|
||||
NewPasswordConfirm: Potvrzení hesla
|
||||
NextButtonText: Další
|
||||
ResendButtonText: Znovu poslat kód
|
||||
|
||||
InitUserDone:
|
||||
Title: Uživatel aktivován
|
||||
Description: E-mail ověřen a heslo úspěšně nastaveno
|
||||
NextButtonText: Další
|
||||
CancelButtonText: Zrušit
|
||||
|
||||
InitMFAPrompt:
|
||||
Title: Nastavení 2-faktorové autentizace
|
||||
Description: 2-faktorová autentizace vám poskytuje další zabezpečení pro váš uživatelský účet. Tím je zajištěno, že k vašemu účtu máte přístup pouze vy.
|
||||
Provider0: Aplikace pro ověřování (např. Google/Microsoft Authenticator, Authy)
|
||||
Provider1: Zařízením závislé (např. FaceID, Windows Hello, Otisk prstu)
|
||||
Provider3: OTP SMS
|
||||
Provider4: OTP E-mail
|
||||
NextButtonText: Další
|
||||
SkipButtonText: Přeskočit
|
||||
|
||||
InitMFAOTP:
|
||||
Title: 2-Faktorové ověření
|
||||
Description: Vytvořte svou 2-faktorovou autentizaci. Stáhněte si ověřovací aplikaci, pokud ji ještě nemáte.
|
||||
OTPDescription: Naskenujte kód svou ověřovací aplikací (např. Google/Microsoft Authenticator, Authy) nebo zkopírujte tajemství a vložte níže vygenerovaný kód.
|
||||
SecretLabel: Tajemství
|
||||
CodeLabel: Kód
|
||||
NextButtonText: Další
|
||||
CancelButtonText: Zrušit
|
||||
|
||||
InitMFAOTPSMS:
|
||||
Title: 2-Faktorové ověření
|
||||
DescriptionPhone: Vytvořte svou 2-faktorovou autentizaci. Zadejte své telefonní číslo k jeho ověření.
|
||||
DescriptionCode: Vytvořte svou 2-faktorovou autentizaci. Zadejte obdržený kód k ověření vašeho telefonního čísla.
|
||||
PhoneLabel: Telefon
|
||||
CodeLabel: Kód
|
||||
EditButtonText: Upravit
|
||||
ResendButtonText: Znovu poslat kód
|
||||
NextButtonText: Další
|
||||
|
||||
InitMFAU2F:
|
||||
Title: Přidat bezpečnostní klíč
|
||||
Description: Bezpečnostní klíč je ověřovací metoda, která může být integrována do vašeho telefonu, používat Bluetooth nebo se přímo zapojit do USB portu vašeho počítače.
|
||||
TokenNameLabel: Název bezpečnostního klíče / zařízení
|
||||
NotSupported: WebAuthN není podporován vaším prohlížečem. Ujistěte se, že máte aktuální verzi, nebo použijte jiný (např. Chrome, Safari, Firefox)
|
||||
RegisterTokenButtonText: Přidat bezpečnostní klíč
|
||||
ErrorRetry: Zkuste to znovu, vytvořte novou výzvu nebo vyberte jinou metodu.
|
||||
|
||||
InitMFADone:
|
||||
Title: 2-Faktor ověřen
|
||||
Description: Skvělé! Úspěšně jste nastavili svou 2-faktorovou autentizaci a váš účet je nyní mnohem bezpečnější. Faktor musí být zadán při každém přihlášení.
|
||||
NextButtonText: Další
|
||||
CancelButtonText: Zrušit
|
||||
|
||||
MFAProvider:
|
||||
Provider0: Aplikace pro ověřování (např. Google/Microsoft Authenticator, Authy)
|
||||
Provider1: Zařízením závislé (např. FaceID, Windows Hello, Otisk prstu)
|
||||
Provider3: OTP SMS
|
||||
Provider4: OTP E-mail
|
||||
ChooseOther: nebo vyberte jinou možnost
|
||||
|
||||
VerifyMFAOTP:
|
||||
Title: Ověřte 2-Faktor
|
||||
Description: Ověřte váš druhý faktor
|
||||
CodeLabel: Kód
|
||||
NextButtonText: Další
|
||||
|
||||
VerifyOTP:
|
||||
Title: Ověřte 2-Faktor
|
||||
Description: Ověřte váš druhý faktor
|
||||
CodeLabel: Kód
|
||||
ResendButtonText: Znovu poslat kód
|
||||
NextButtonText: Další
|
||||
|
||||
VerifyMFAU2F:
|
||||
Title: 2-Faktorové ověření
|
||||
Description: Ověřte váš 2-Faktor pomocí registrovaného zařízení (např. FaceID, Windows Hello, Otisk prstu)
|
||||
NotSupported: WebAuthN není podporován vaším prohlížečem. Ujistěte se, že používáte nejnovější verzi, nebo změňte prohlížeč na podporovaný (Chrome, Safari, Firefox)
|
||||
ErrorRetry: Zkuste to znovu, vytvořte nový požadavek nebo vyberte jinou metodu.
|
||||
ValidateTokenButtonText: Ověřte 2-Faktor
|
||||
|
||||
Passwordless:
|
||||
Title: Bezheslové přihlášení
|
||||
Description: Přihlaste se pomocí ověřovacích metod poskytnutých vaším zařízením, jako je FaceID, Windows Hello nebo Otisk prstu.
|
||||
NotSupported: WebAuthN není podporován vaším prohlížečem. Ujistěte se, že máte aktuální verzi, nebo použijte jiný (např. Chrome, Safari, Firefox).
|
||||
ErrorRetry: Zkuste to znovu, vytvořte novou výzvu nebo vyberte jinou metodu.
|
||||
LoginWithPwButtonText: Přihlásit se heslem
|
||||
ValidateTokenButtonText: Přihlásit se bez hesla
|
||||
|
||||
PasswordlessPrompt:
|
||||
Title: Nastavení bezheslového přihlášení
|
||||
Description: Chcete nastavit přihlášení bez hesla? (Ověřovací metody vašeho zařízení, jako je FaceID, Windows Hello nebo Otisk prstu)
|
||||
DescriptionInit: Musíte nastavit přihlášení bez hesla. Použijte odkaz, který jste obdrželi, pro registraci vašeho zařízení.
|
||||
PasswordlessButtonText: Přihlásit se bez hesla
|
||||
NextButtonText: Další
|
||||
SkipButtonText: Přeskočit
|
||||
|
||||
PasswordlessRegistration:
|
||||
Title: Nastavení bezheslového přihlášení
|
||||
Description: Přidejte své ověření zadáním názvu (např. MůjMobil, MacBook atd.) a poté kliknutím na tlačítko 'Registrovat bez hesla' níže.
|
||||
TokenNameLabel: Název zařízení
|
||||
NotSupported: WebAuthN není podporován vaším prohlížečem. Ujistěte se, že máte aktuální verzi, nebo použijte jiný (např. Chrome, Safari, Firefox).
|
||||
RegisterTokenButtonText: Registrovat bez hesla
|
||||
ErrorRetry: Zkuste to znovu, vytvořte novou výzvu nebo vyberte jinou metodu.
|
||||
|
||||
PasswordlessRegistrationDone:
|
||||
Title: Nastavení bezheslového přihlášení dokončeno
|
||||
Description: Zařízení pro přihlášení bez hesla úspěšně přidáno.
|
||||
DescriptionClose: Nyní můžete toto okno zavřít.
|
||||
NextButtonText: Další
|
||||
CancelButtonText: Zrušit
|
||||
|
||||
PasswordChange:
|
||||
Title: Změna hesla
|
||||
Description: Změňte si heslo. Zadejte své staré a nové heslo.
|
||||
OldPasswordLabel: Staré heslo
|
||||
NewPasswordLabel: Nové heslo
|
||||
NewPasswordConfirmLabel: Potvrzení hesla
|
||||
CancelButtonText: Zrušit
|
||||
NextButtonText: Další
|
||||
Footer: Patička
|
||||
|
||||
PasswordChangeDone:
|
||||
Title: Změna hesla
|
||||
Description: Vaše heslo bylo úspěšně změněno.
|
||||
NextButtonText: Další
|
||||
|
||||
PasswordResetDone:
|
||||
Title: Odkaz na resetování hesla odeslán
|
||||
Description: Pro dokončení změny hesla zkontrolujte váš e-mail a postupujte podle instrukcí.
|
||||
NextButtonText: Další
|
||||
|
||||
EmailVerification:
|
||||
Title: Ověření e-mailu
|
||||
Description: Poslali jsme vám e-mail pro ověření vaší adresy. Zadejte kód do níže uvedeného formuláře.
|
||||
CodeLabel: Kód
|
||||
NextButtonText: Další
|
||||
ResendButtonText: Znovu poslat kód
|
||||
|
||||
EmailVerificationDone:
|
||||
Title: Ověření e-mailu
|
||||
Description: Vaše e-mailová adresa byla úspěšně ověřena.
|
||||
NextButtonText: Další
|
||||
CancelButtonText: Zrušit
|
||||
LoginButtonText: Přihlásit se
|
||||
|
||||
RegisterOption:
|
||||
Title: Možnosti registrace
|
||||
Description: Vyberte si, jak se chcete zaregistrovat
|
||||
RegisterUsernamePasswordButtonText: Pomocí uživatelského jména a hesla
|
||||
ExternalLoginDescription: nebo se zaregistrujte s externím uživatelem
|
||||
LoginButtonText: Přihlásit se
|
||||
|
||||
RegistrationUser:
|
||||
Title: Registrace
|
||||
Description: Zadejte své uživatelské údaje. Váš e-mail bude použit jako vaše přihlašovací jméno.
|
||||
DescriptionOrgRegister: Zadejte své uživatelské údaje.
|
||||
EmailLabel: E-mail
|
||||
UsernameLabel: Uživatelské jméno
|
||||
FirstnameLabel: Křestní jméno
|
||||
LastnameLabel: Příjmení
|
||||
LanguageLabel: Jazyk
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Pohlaví
|
||||
Female: Žena
|
||||
Male: Muž
|
||||
Diverse: různorodé / X
|
||||
PasswordLabel: Heslo
|
||||
PasswordConfirmLabel: Potvrzení hesla
|
||||
TosAndPrivacyLabel: Obchodní podmínky
|
||||
TosConfirm: Souhlasím s
|
||||
TosLinkText: obchodními podmínkami
|
||||
PrivacyConfirm: Souhlasím se
|
||||
PrivacyLinkText: zásadami ochrany osobních údajů
|
||||
ExternalLogin: nebo se zaregistrujte s externím uživatelem
|
||||
BackButtonText: Přihlásit se
|
||||
NextButtonText: Další
|
||||
|
||||
ExternalRegistrationUserOverview:
|
||||
Title: Registrace externího uživatele
|
||||
Description: Vaše uživatelské údaje byly převzaty z vybraného poskytovatele. Nyní je můžete změnit nebo doplnit.
|
||||
EmailLabel: E-mail
|
||||
UsernameLabel: Uživatelské jméno
|
||||
FirstnameLabel: Křestní jméno
|
||||
LastnameLabel: Příjmení
|
||||
NicknameLabel: Přezdívka
|
||||
PhoneLabel: Telefonní číslo
|
||||
LanguageLabel: Jazyk
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Obchodní podmínky
|
||||
TosConfirm: Souhlasím s
|
||||
TosLinkText: obchodními podmínkami
|
||||
PrivacyConfirm: Souhlasím se
|
||||
PrivacyLinkText: zásadami ochrany osobních údajů
|
||||
ExternalLogin: nebo se zaregistrujte s externím uživatelem
|
||||
BackButtonText: Zpět
|
||||
NextButtonText: Uložit
|
||||
|
||||
RegistrationOrg:
|
||||
Title: Registrace organizace
|
||||
Description: Zadejte název vaší organizace a uživatelské údaje.
|
||||
OrgNameLabel: Název organizace
|
||||
EmailLabel: E-mail
|
||||
UsernameLabel: Uživatelské jméno
|
||||
FirstnameLabel: Křestní jméno
|
||||
LastnameLabel: Příjmení
|
||||
PasswordLabel: Heslo
|
||||
PasswordConfirmLabel: Potvrzení hesla
|
||||
TosAndPrivacyLabel: Obchodní podmínky
|
||||
TosConfirm: Souhlasím s
|
||||
TosLinkText: obchodními podmínkami
|
||||
PrivacyConfirm: Souhlasím se
|
||||
PrivacyLinkText: zásadami ochrany osobních údajů
|
||||
SaveButtonText: Vytvořit organizaci
|
||||
|
||||
LoginSuccess:
|
||||
Title: Úspěšné přihlášení
|
||||
AutoRedirectDescription: Budete automaticky přesměrováni zpět do vaší aplikace. Pokud ne, klikněte na tlačítko níže. Poté můžete okno zavřít.
|
||||
RedirectedDescription: Nyní můžete okno zavřít.
|
||||
NextButtonText: Další
|
||||
|
||||
LogoutDone:
|
||||
Title: Odhlášení proběhlo úspěšně
|
||||
Description: Byli jste úspěšně odhlášeni.
|
||||
LoginButtonText: Přihlásit se
|
||||
|
||||
LinkingUsersDone:
|
||||
Title: Propojení uživatele
|
||||
Description: Uživatel propojen.
|
||||
CancelButtonText: Zrušit
|
||||
NextButtonText: Další
|
||||
|
||||
ExternalNotFound:
|
||||
Title: Externí uživatel nenalezen
|
||||
Description: Externí uživatel nebyl nalezen. Chcete propojit svého uživatele nebo automaticky zaregistrovat nového?
|
||||
LinkButtonText: Propojit
|
||||
AutoRegisterButtonText: Registrovat
|
||||
TosAndPrivacyLabel: Obchodní podmínky
|
||||
TosConfirm: Souhlasím s
|
||||
TosLinkText: obchodními podmínkami
|
||||
PrivacyConfirm: Souhlasím s
|
||||
PrivacyLinkText: zásadami ochrany osobních údajů
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
DeviceAuth:
|
||||
Title: Autorizace zařízení
|
||||
UserCode:
|
||||
Label: Uživatelský kód
|
||||
Description: Zadejte uživatelský kód zobrazený na zařízení.
|
||||
ButtonNext: Další
|
||||
Action:
|
||||
Description: Povolte přístup zařízení.
|
||||
GrantDevice: chystáte se povolit zařízení
|
||||
AccessToScopes: přístup k následujícím rozsahům
|
||||
Button:
|
||||
Allow: Povolit
|
||||
Deny: Zamítnout
|
||||
Done:
|
||||
Description: Hotovo.
|
||||
Approved: Autorizace zařízení schválena. Nyní se můžete vrátit k zařízení.
|
||||
Denied: Autorizace zařízení zamítnuta. Nyní se můžete vrátit k zařízení.
|
||||
|
||||
Footer:
|
||||
PoweredBy: Provozováno pomocí
|
||||
Tos: Obchodní podmínky
|
||||
PrivacyPolicy: Zásady ochrany osobních údajů
|
||||
Help: Pomoc
|
||||
SupportEmail: E-mailová podpora
|
||||
|
||||
SignIn: Přihlašte se pomocí {{.Provider}}
|
||||
|
||||
Errors:
|
||||
Internal: Došlo k interní chybě
|
||||
AuthRequest:
|
||||
NotFound: Authrequest nebyl nalezen
|
||||
UserAgentNotCorresponding: User Agent neodpovídá
|
||||
UserAgentNotFound: ID User Agenta nebylo nalezeno
|
||||
TokenNotFound: Token nebyl nalezen
|
||||
RequestTypeNotSupported: Typ požadavku není podporován
|
||||
MissingParameters: Chybějící požadované parametry
|
||||
User:
|
||||
NotFound: Uživatel nebyl nalezen
|
||||
AlreadyExists: Uživatel již existuje
|
||||
Inactive: Uživatel je neaktivní
|
||||
NotFoundOnOrg: Uživatel nebyl nalezen ve zvolené organizaci
|
||||
NotAllowedOrg: Uživatel není členem požadované organizace
|
||||
NotMatchingUserID: ID uživatele a uživatele v authrequestu se neshodují
|
||||
UserIDMissing: ID uživatele chybí
|
||||
Invalid: Neplatná uživatelská data
|
||||
DomainNotAllowedAsUsername: Doména je již rezervována a nelze ji použít jako uživatelské jméno
|
||||
NotAllowedToLink: Uživatel nemá povoleno propojení s externím poskytovatelem přihlášení
|
||||
Profile:
|
||||
NotFound: Profil nenalezen
|
||||
NotChanged: Profil nebyl změněn
|
||||
Empty: Profil je prázdný
|
||||
FirstNameEmpty: Jméno v profilu je prázdné
|
||||
LastNameEmpty: Příjmení v profilu je prázdné
|
||||
IDMissing: Chybí ID profilu
|
||||
Email:
|
||||
NotFound: E-mail nenalezen
|
||||
Invalid: E-mail je neplatný
|
||||
AlreadyVerified: E-mail je již ověřen
|
||||
NotChanged: E-mail nebyl změněn
|
||||
Empty: E-mail je prázdný
|
||||
IDMissing: Chybí ID e-mailu
|
||||
Phone:
|
||||
NotFound: Telefon nenalezen
|
||||
Invalid: Telefon je neplatný
|
||||
AlreadyVerified: Telefon již byl ověřen
|
||||
Empty: Telefon je prázdný
|
||||
NotChanged: Telefon nebyl změněn
|
||||
Address:
|
||||
NotFound: Adresa nenalezena
|
||||
NotChanged: Adresa nebyla změněna
|
||||
Username:
|
||||
AlreadyExists: Uživatelské jméno již je obsazené
|
||||
Reserved: Uživatelské jméno není volné
|
||||
Empty: Uživatelské jméno je prázdné
|
||||
Password:
|
||||
ConfirmationWrong: Potvrzení hesla je nesprávné
|
||||
Empty: Heslo je prázdné
|
||||
Invalid: Heslo je neplatné
|
||||
InvalidAndLocked: Heslo je neplatné a uživatel je uzamčen, kontaktujte svého správce.
|
||||
NotChanged: Nové heslo nesmí být stejné jako stávající heslo
|
||||
UsernameOrPassword:
|
||||
Invalid: Uživatelské jméno nebo heslo je neplatné
|
||||
PasswordComplexityPolicy:
|
||||
NotFound: Zásady složitosti hesla nenalezeny
|
||||
MinLength: Heslo je příliš krátké
|
||||
HasLower: Heslo musí obsahovat malé písmeno
|
||||
HasUpper: Heslo musí obsahovat velké písmeno
|
||||
HasNumber: Heslo musí obsahovat číslo
|
||||
HasSymbol: Heslo musí obsahovat symbol
|
||||
Code:
|
||||
Expired: Kód vypršel
|
||||
Invalid: Kód je neplatný
|
||||
Empty: Kód je prázdný
|
||||
CryptoCodeNil: Krypto kód je nulový
|
||||
NotFound: Kód nebyl nalezen
|
||||
GeneratorAlgNotSupported: Nepodporovaný algoritmus generátoru
|
||||
EmailVerify:
|
||||
UserIDEmpty: ID uživatele je prázdné
|
||||
ExternalData:
|
||||
CouldNotRead: Externí data nebylo možné správně přečíst
|
||||
MFA:
|
||||
NoProviders: Žádní dostupní poskytovatelé vícefaktorového ověřování
|
||||
OTP:
|
||||
AlreadyReady: Vícefaktorové OTP (jednorázové heslo) je již nastaveno
|
||||
NotExisting: Vícefaktorové OTP (jednorázové heslo) neexistuje
|
||||
InvalidCode: Neplatný kód
|
||||
NotReady: Vícefaktorové OTP (jednorázové heslo) není připraveno
|
||||
Locked: Uživatel je uzamčen
|
||||
SomethingWentWrong: Něco se pokazilo
|
||||
NotActive: Uživatel není aktivní
|
||||
ExternalIDP:
|
||||
IDPTypeNotImplemented: Typ IDP není implementován
|
||||
NotAllowed: Externí poskytovatel přihlášení není povolen
|
||||
IDPConfigIDEmpty: ID konfigurace poskytovatele identity je prázdné
|
||||
ExternalUserIDEmpty: Externí ID uživatele je prázdné
|
||||
UserDisplayNameEmpty: Zobrazované jméno uživatele je prázdné
|
||||
NoExternalUserData: Nebyla přijata žádná externí uživatelská data
|
||||
CreationNotAllowed: Vytvoření nového uživatele není na tomto poskytovateli povoleno
|
||||
LinkingNotAllowed: Propojení uživatele není na tomto poskytovateli povoleno
|
||||
GrantRequired: Přihlášení není možné. Uživatel musí mít alespoň jeden oprávnění na aplikaci. Prosím, kontaktujte svého správce.
|
||||
ProjectRequired: Přihlášení není možné. Organizace uživatele musí být přidělena k projektu. Prosím, kontaktujte svého správce.
|
||||
IdentityProvider:
|
||||
InvalidConfig: Konfigurace poskytovatele identity je neplatná
|
||||
IAM:
|
||||
LockoutPolicy:
|
||||
NotExisting: Zásady uzamčení neexistují
|
||||
Org:
|
||||
LoginPolicy:
|
||||
RegistrationNotAllowed: Registrace není povolena
|
||||
DeviceAuth:
|
||||
NotExisting: Kód uživatelského zařízení neexistuje
|
||||
|
||||
optional: (volitelné)
|
||||
@@ -245,6 +245,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Geschlecht
|
||||
Female: weiblich
|
||||
Male: männlich
|
||||
@@ -281,6 +283,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Allgemeine Geschäftsbedingungen und Datenschutz
|
||||
TosConfirm: Ich akzeptiere die
|
||||
TosLinkText: AGB
|
||||
@@ -345,7 +349,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
DeviceAuth:
|
||||
Title: Gerätezulassung
|
||||
UserCode:
|
||||
|
||||
@@ -246,6 +246,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Gender
|
||||
Female: Female
|
||||
Male: Male
|
||||
@@ -282,6 +284,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Terms and conditions
|
||||
TosConfirm: I accept the
|
||||
TosLinkText: TOS
|
||||
@@ -346,7 +350,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
DeviceAuth:
|
||||
Title: Device Authorization
|
||||
UserCode:
|
||||
|
||||
@@ -246,6 +246,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Género
|
||||
Female: Mujer
|
||||
Male: Hombre
|
||||
@@ -282,6 +284,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Términos y condiciones
|
||||
TosConfirm: Acepto los
|
||||
TosLinkText: TDS
|
||||
@@ -346,6 +350,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
|
||||
Footer:
|
||||
PoweredBy: Powered By
|
||||
|
||||
@@ -246,6 +246,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Genre
|
||||
Female: Femme
|
||||
Male: Homme
|
||||
@@ -282,6 +284,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Termes et conditions
|
||||
TosConfirm: J'accepte les
|
||||
TosLinkText: TOS
|
||||
@@ -346,6 +350,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
|
||||
DeviceAuth:
|
||||
Title: Autorisation de l'appareil
|
||||
|
||||
@@ -246,6 +246,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Genere
|
||||
Female: Femminile
|
||||
Male: Maschile
|
||||
@@ -282,6 +284,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Termini di servizio
|
||||
TosConfirm: Accetto i
|
||||
TosLinkText: Termini di servizio
|
||||
@@ -346,6 +350,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
|
||||
DeviceAuth:
|
||||
Title: Autorizzazione del dispositivo
|
||||
|
||||
@@ -238,6 +238,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: 性別
|
||||
Female: 女性
|
||||
Male: 男性
|
||||
@@ -274,6 +276,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: 利用規約
|
||||
TosConfirm: 私は利用規約を承諾します。
|
||||
TosLinkText: TOS
|
||||
@@ -338,6 +342,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
|
||||
DeviceAuth:
|
||||
Title: デバイス認証
|
||||
|
||||
@@ -246,6 +246,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Пол
|
||||
Female: Женски
|
||||
Male: Машки
|
||||
@@ -282,6 +284,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Правила и услови
|
||||
TosConfirm: Се согласувам со
|
||||
TosLinkText: правилата за користење
|
||||
@@ -346,6 +350,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
|
||||
DeviceAuth:
|
||||
Title: Овластување преку уред
|
||||
|
||||
@@ -246,6 +246,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Płeć
|
||||
Female: Kobieta
|
||||
Male: Mężczyzna
|
||||
@@ -282,6 +284,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Warunki i zasady
|
||||
TosConfirm: Akceptuję
|
||||
TosLinkText: Warunki korzystania
|
||||
@@ -346,6 +350,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
|
||||
DeviceAuth:
|
||||
Title: Autoryzacja urządzenia
|
||||
|
||||
@@ -242,6 +242,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Gênero
|
||||
Female: Feminino
|
||||
Male: Masculino
|
||||
@@ -267,16 +269,19 @@ ExternalRegistrationUserOverview:
|
||||
NicknameLabel: Apelido
|
||||
PhoneLabel: Número de telefone
|
||||
LanguageLabel: Idioma
|
||||
German: Alemão
|
||||
English: Inglês
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Francês
|
||||
Chinese: Chinês simplificado
|
||||
Japanese: Japonês
|
||||
Polish: Polonês
|
||||
Spanish: Espanhol
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Termos e condições
|
||||
TosConfirm: Eu aceito os
|
||||
TosLinkText: termos de serviço
|
||||
@@ -330,17 +335,20 @@ ExternalNotFound:
|
||||
TosLinkText: termos de serviço
|
||||
PrivacyConfirm: Eu aceito a
|
||||
PrivacyLinkText: política de privacidade
|
||||
German: Alemão
|
||||
English: Inglês
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Francês
|
||||
Chinese: Chinês simplificado
|
||||
Polish: Polonês
|
||||
Japanese: Japonês
|
||||
Spanish: Espanhol
|
||||
Bulgarian: Búlgaro
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
|
||||
DeviceAuth:
|
||||
Title: Autorização de dispositivo
|
||||
UserCode:
|
||||
|
||||
487
internal/api/ui/login/static/i18n/ru.yaml
Normal file
487
internal/api/ui/login/static/i18n/ru.yaml
Normal file
@@ -0,0 +1,487 @@
|
||||
Login:
|
||||
Title: С возвращением!
|
||||
Description: Введите свои данные для входа.
|
||||
TitleLinking: Логин для привязки пользователей
|
||||
DescriptionLinking: Введите свои данные для входа, чтобы связать внешнего пользователя.
|
||||
LoginNameLabel: Имя пользователя
|
||||
UsernamePlaceHolder: Имя пользователя
|
||||
LoginnamePlaceHolder: username@domain
|
||||
ExternalUserDescription: Войдите в систему с внешним пользователем.
|
||||
MustBeMemberOfOrg: Пользователь должен быть членом {{.OrgName}} организации.
|
||||
RegisterButtonText: регистрировать
|
||||
NextButtonText: следующий
|
||||
|
||||
LDAP:
|
||||
Title: Войти
|
||||
Description: Введите ваши данные дял входа.
|
||||
LoginNameLabel: Имя пользователя
|
||||
PasswordLabel: Пароль
|
||||
NextButtonText: следующий
|
||||
|
||||
SelectAccount:
|
||||
Title: Выберите учетную запись
|
||||
Description: Используйте свой аккаунт
|
||||
TitleLinking: Выберите учетную запись для привязки пользователя
|
||||
DescriptionLinking: Выберите свою учетную запись для связи с внешним пользователем.
|
||||
OtherUser: Другой пользователь
|
||||
SessionState0: активный
|
||||
SessionState1: Вышел из системы
|
||||
MustBeMemberOfOrg: Пользователь должен быть членом {{.OrgName}} организации.
|
||||
|
||||
Password:
|
||||
Title: Пароль
|
||||
Description: Введите свои данные для входа.
|
||||
PasswordLabel: Пароль
|
||||
MinLength: Минимальная длина
|
||||
HasUppercase: Прописная буква
|
||||
HasLowercase: Строчная буква
|
||||
HasNumber: Число
|
||||
HasSymbol: Символ
|
||||
Confirmation: Подтверждающее совпадение
|
||||
ResetLinkText: Сброс пароля
|
||||
BackButtonText: Назад
|
||||
NextButtonText: следующий
|
||||
|
||||
UsernameChange:
|
||||
Title: Изменить имя пользователя
|
||||
Description: Установите новое имя пользователя
|
||||
UsernameLabel: Имя пользователя
|
||||
CancelButtonText: Отмена
|
||||
NextButtonText: следующий
|
||||
|
||||
UsernameChangeDone:
|
||||
Title: Имя пользователя изменено
|
||||
Description: Ваше имя пользователя было успешно изменено.
|
||||
NextButtonText: следующий
|
||||
|
||||
InitPassword:
|
||||
Title: Установить пароль
|
||||
Description: Вы получили код, который необходимо ввести в форму ниже, чтобы установить новый пароль.
|
||||
CodeLabel: Код
|
||||
NewPasswordLabel: Новый пароль
|
||||
NewPasswordConfirmLabel: Подтвердите пароль
|
||||
ResendButtonText: Повторная отправка кода
|
||||
NextButtonText: следующий
|
||||
|
||||
InitPasswordDone:
|
||||
Title: Установка пароля
|
||||
Description: Пароль успешно установлен
|
||||
NextButtonText: следующий
|
||||
CancelButtonText: Отмена
|
||||
|
||||
InitUser:
|
||||
Title: Активировать пользователя
|
||||
Description: Подтвердите свой адрес электронной почты с помощью приведенного ниже кода и установите пароль.
|
||||
CodeLabel: Код
|
||||
NewPasswordLabel: Новый пароль
|
||||
NewPasswordConfirm: Подтвердите пароль
|
||||
NextButtonText: следующий
|
||||
ResendButtonText: Повторная отправка кода
|
||||
|
||||
InitUserDone:
|
||||
Title: Пользователь активирован
|
||||
Description: Адрес электронной почты подтвержден и пароль успешно установлен
|
||||
NextButtonText: следующий
|
||||
CancelButtonText: Отмена
|
||||
|
||||
InitMFAPrompt:
|
||||
Title: 2-факторная настройка
|
||||
Description: 2-факторная аутентификация обеспечивает дополнительную безопасность вашей учетной записи пользователя. Это гарантирует, что только у вас есть доступ к вашей учетной записи.
|
||||
Provider0: Приложение Authenticator (например, Google/Microsoft Authenticator, Authy)
|
||||
Provider1: Зависит от устройства (например, FaceID, Windows Hello, отпечаток пальца)
|
||||
Provider3: OTP SMS
|
||||
Provider4: Электронная почта OTP
|
||||
NextButtonText: следующий
|
||||
SkipButtonText: скип
|
||||
|
||||
InitMFAOTP:
|
||||
Title: 2-факторная верификация
|
||||
Description: Создайте свой 2-фактор. Загрузите приложение для проверки подлинности, если у вас его еще нет.
|
||||
OTPDescription: Отсканируйте код с помощью приложения для аутентификации (например, Google/Microsoft Authenticator, Authy) или скопируйте секрет и вставьте сгенерированный код ниже.
|
||||
SecretLabel: Секрет
|
||||
CodeLabel: Код
|
||||
NextButtonText: следующий
|
||||
CancelButtonText: Отмена
|
||||
|
||||
InitMFAOTPSMS:
|
||||
Title: 2-факторная верификация
|
||||
DescriptionPhone: Создайте свой 2-фактор. Введите свой номер телефона, чтобы подтвердить его.
|
||||
DescriptionCode: Создайте свой 2-фактор. Введите полученный код, чтобы подтвердить свой номер телефона.
|
||||
PhoneLabel: Телефон
|
||||
CodeLabel: Код
|
||||
EditButtonText: редактировать
|
||||
ResendButtonText: Повторная отправка кода
|
||||
NextButtonText: следующий
|
||||
|
||||
InitMFAU2F:
|
||||
Title: Добавление ключа безопасности
|
||||
Description: Ключ безопасности — это метод проверки, который можно встроить в телефон, использовать Bluetooth или подключить непосредственно к USB-порту компьютера.
|
||||
TokenNameLabel: Имя ключа безопасности / устройства
|
||||
NotSupported: WebAuthN не поддерживается вашим браузером. Пожалуйста, убедитесь, что он обновлен или используйте другой (например, Chrome, Safari, Firefox)
|
||||
RegisterTokenButtonText: Добавление ключа безопасности
|
||||
ErrorRetry: Повторите попытку, создайте новую задачу или выберите другой метод.
|
||||
|
||||
InitMFADone:
|
||||
Title: 2-факторная проверка
|
||||
Description: Замечательно! Вы только что успешно настроили свой 2-фактор и сделали свою учетную запись более безопасной. Фактор должен быть введен при каждом входе в систему.
|
||||
NextButtonText: следующий
|
||||
CancelButtonText: Отмена
|
||||
|
||||
MFAProvider:
|
||||
Provider0: Приложение Authenticator (например, Google/Microsoft Authenticator, Authy)
|
||||
Provider1: Зависит от устройства (например, FaceID, Windows Hello, отпечаток пальца)
|
||||
Provider3: OTP SMS
|
||||
Provider4: Электронная почта OTP
|
||||
ChooseOther: или выберите другой вариант
|
||||
|
||||
VerifyMFAOTP:
|
||||
Title: Проверка 2-фактора
|
||||
Description: Проверьте свой второй фактор
|
||||
CodeLabel: Код
|
||||
NextButtonText: следующий
|
||||
|
||||
VerifyOTP:
|
||||
Title: Проверка 2-фактора
|
||||
Description: Проверьте свой второй фактор
|
||||
CodeLabel: Код
|
||||
ResendButtonText: Повторная отправка кода
|
||||
NextButtonText: следующий
|
||||
|
||||
VerifyMFAU2F:
|
||||
Title: 2-факторная верификация
|
||||
Description: Подтвердите свой 2-фактор с зарегистрированным устройством (например, FaceID, Windows Hello, отпечаток пальца)
|
||||
NotSupported: WebAuthN не поддерживается вашим браузером. Убедитесь, что вы используете последнюю версию или измените браузер на поддерживаемый (Chrome, Safari, Firefox)
|
||||
ErrorRetry: Повторите попытку, создайте новый запрос или выберите другой метод.
|
||||
ValidateTokenButtonText: Проверить второй фактор
|
||||
|
||||
Passwordless:
|
||||
Title: Вход без пароля
|
||||
Description: Войдите в систему с помощью методов аутентификации, предоставляемых вашим устройством, таких как FaceID, Windows Hello или отпечаток пальца.
|
||||
NotSupported: WebAuthN не поддерживается вашим браузером. Пожалуйста, убедитесь, что он обновлен или используйте другой (например, Chrome, Safari, Firefox)
|
||||
ErrorRetry: Повторите попытку, создайте новую задачу или выберите другой метод.
|
||||
LoginWithPwButtonText: Вход с паролем
|
||||
ValidateTokenButtonText: Вход без пароля
|
||||
|
||||
PasswordlessPrompt:
|
||||
Title: Настройка без пароля
|
||||
Description: Хотите настроить вход без пароля? (Методы аутентификации вашего устройства, такие как FaceID, Windows Hello или отпечаток пальца)
|
||||
DescriptionInit: Вам необходимо настроить вход без пароля. Используйте ссылку, которую вам дали, чтобы зарегистрировать свое устройство.
|
||||
PasswordlessButtonText: Переход без пароля
|
||||
NextButtonText: следующий
|
||||
SkipButtonText: скип
|
||||
|
||||
PasswordlessRegistration:
|
||||
Title: Настройка без пароля
|
||||
Description: Добавьте свою аутентификацию, указав имя (например, MyMobilePhone, MacBook и т. д.), а затем нажав кнопку «Зарегистрироваться без пароля» ниже.
|
||||
TokenNameLabel: Имя устройства
|
||||
NotSupported: WebAuthN не поддерживается вашим браузером. Пожалуйста, убедитесь, что он обновлен или используйте другой (например, Chrome, Safari, Firefox)
|
||||
RegisterTokenButtonText: Регистрация без пароля
|
||||
ErrorRetry: Повторите попытку, создайте новую задачу или выберите другой метод.
|
||||
|
||||
PasswordlessRegistrationDone:
|
||||
Title: Настройка без пароля
|
||||
Description: Устройство для работы без пароля успешно добавлено.
|
||||
DescriptionClose: Теперь вы можете закрыть это окно.
|
||||
NextButtonText: следующий
|
||||
CancelButtonText: Отмена
|
||||
|
||||
PasswordChange:
|
||||
Title: Смена пароля
|
||||
Description: Смените пароль. Введите свой старый и новый пароль.
|
||||
OldPasswordLabel: Старый пароль
|
||||
NewPasswordLabel: Новый пароль
|
||||
NewPasswordConfirmLabel: Подтверждение пароля
|
||||
CancelButtonText: Отмена
|
||||
NextButtonText: следующий
|
||||
Footer: Нижний колонтитул
|
||||
|
||||
PasswordChangeDone:
|
||||
Title: Смена пароля
|
||||
Description: Ваш пароль был успешно изменен.
|
||||
NextButtonText: следующий
|
||||
|
||||
PasswordResetDone:
|
||||
Title: Ссылка для сброса пароля отправлена
|
||||
Description: Проверьте свою электронную почту, чтобы сбросить пароль.
|
||||
NextButtonText: следующий
|
||||
|
||||
EmailVerification:
|
||||
Title: Проверка электронной почты
|
||||
Description: Мы отправили вам электронное письмо для подтверждения вашего адреса. Пожалуйста, введите код в форму ниже.
|
||||
CodeLabel: Код
|
||||
NextButtonText: следующий
|
||||
ResendButtonText: Повторная отправка кода
|
||||
|
||||
EmailVerificationDone:
|
||||
Title: Проверка электронной почты
|
||||
Description: Ваш адрес электронной почты успешно подтвержден.
|
||||
NextButtonText: следующий
|
||||
CancelButtonText: Отмена
|
||||
LoginButtonText: логин
|
||||
|
||||
RegisterOption:
|
||||
Title: Варианты регистрации
|
||||
Description: Выберите, как вы хотите зарегистрироваться
|
||||
RegisterUsernamePasswordButtonText: С именем пользователя, паролем
|
||||
ExternalLoginDescription: или зарегистрироваться у внешнего пользователя
|
||||
LoginButtonText: логин
|
||||
|
||||
RegistrationUser:
|
||||
Title: Регистрация
|
||||
Description: Введите свои данные пользователя. Ваш адрес электронной почты будет использоваться в качестве имени пользователя.
|
||||
DescriptionOrgRegister: Введите свои данные пользователя.
|
||||
EmailLabel: Отправить по электронной почте
|
||||
UsernameLabel: Имя пользователя
|
||||
FirstnameLabel: Имя
|
||||
LastnameLabel: Фамилия
|
||||
LanguageLabel: Язык
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: Род
|
||||
Female: Женский
|
||||
Male: Мужской
|
||||
Diverse: разное / X
|
||||
PasswordLabel: Пароль
|
||||
PasswordConfirmLabel: Подтверждение пароля
|
||||
TosAndPrivacyLabel: Правила и условия
|
||||
TosConfirm: Я принимаю
|
||||
TosLinkText: ТОТ
|
||||
PrivacyConfirm: Я принимаю
|
||||
PrivacyLinkText: политика конфиденциальности
|
||||
ExternalLogin: или зарегистрироваться у внешнего пользователя
|
||||
BackButtonText: логин
|
||||
NextButtonText: следующий
|
||||
|
||||
ExternalRegistrationUserOverview:
|
||||
Title: Регистрация внешнего пользователя
|
||||
Description: Мы взяли ваши пользовательские данные у выбранного провайдера. Теперь вы можете изменить или дополнить их.
|
||||
EmailLabel: Отправить по электронной почте
|
||||
UsernameLabel: Имя пользователя
|
||||
FirstnameLabel: Имя
|
||||
LastnameLabel: Фамилия
|
||||
NicknameLabel: Прозвище
|
||||
PhoneLabel: Номер телефона
|
||||
LanguageLabel: Язык
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: Правила и условия
|
||||
TosConfirm: Я принимаю
|
||||
TosLinkText: ТОТ
|
||||
PrivacyConfirm: Я принимаю
|
||||
PrivacyLinkText: политика конфиденциальности
|
||||
ExternalLogin: или зарегистрироваться у внешнего пользователя
|
||||
BackButtonText: Назад
|
||||
NextButtonText: спасать
|
||||
|
||||
RegistrationOrg:
|
||||
Title: Регистрация организации
|
||||
Description: Введите название организации и данные пользователя.
|
||||
OrgNameLabel: Название организации
|
||||
EmailLabel: Отправить по электронной почте
|
||||
UsernameLabel: Имя пользователя
|
||||
FirstnameLabel: Имя
|
||||
LastnameLabel: Фамилия
|
||||
PasswordLabel: Пароль
|
||||
PasswordConfirmLabel: Подтверждение пароля
|
||||
TosAndPrivacyLabel: Правила и условия
|
||||
TosConfirm: Я принимаю
|
||||
TosLinkText: ТОТ
|
||||
PrivacyConfirm: Я принимаю
|
||||
PrivacyLinkText: политика конфиденциальности
|
||||
SaveButtonText: Создать организацию
|
||||
|
||||
LoginSuccess:
|
||||
Title: Вход в систему выполнен успешно
|
||||
AutoRedirectDescription: Вы будете автоматически перенаправлены обратно к вашему приложению. Если нет, нажмите на кнопку ниже. После этого вы можете закрыть окно.
|
||||
RedirectedDescription: Теперь вы можете закрыть это окно.
|
||||
NextButtonText: следующий
|
||||
|
||||
LogoutDone:
|
||||
Title: Вышел из системы
|
||||
Description: Вы успешно вышли из системы.
|
||||
LoginButtonText: логин
|
||||
|
||||
LinkingUsersDone:
|
||||
Title: Юзерлинкинг
|
||||
Description: Пользовательские ссылки сделаны.
|
||||
CancelButtonText: Отмена
|
||||
NextButtonText: следующий
|
||||
|
||||
ExternalNotFound:
|
||||
Title: Внешний пользователь не найден
|
||||
Description: Внешний пользователь не найден. Вы хотите связать своего пользователя или автоматически зарегистрировать нового.
|
||||
LinkButtonText: Связь
|
||||
AutoRegisterButtonText: регистрировать
|
||||
TosAndPrivacyLabel: Правила и условия
|
||||
TosConfirm: Я принимаю
|
||||
TosLinkText: ТОТ
|
||||
PrivacyConfirm: Я принимаю
|
||||
PrivacyLinkText: политика конфиденциальности
|
||||
German: Deutsch
|
||||
English: English
|
||||
Italian: Italiano
|
||||
French: Français
|
||||
Chinese: 简体中文
|
||||
Polish: Polski
|
||||
Japanese: 日本語
|
||||
Spanish: Español
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
DeviceAuth:
|
||||
Title: Авторизация устройства
|
||||
UserCode:
|
||||
Label: Код пользователя
|
||||
Description: Введите код пользователя, представленный на устройстве.
|
||||
ButtonNext: следующий
|
||||
Action:
|
||||
Description: Предоставьте доступ к устройству.
|
||||
GrantDevice: Вы собираетесь предоставить устройство
|
||||
AccessToScopes: Доступ к следующим областям
|
||||
Button:
|
||||
Allow: разрешать
|
||||
Deny: отрицать
|
||||
Done:
|
||||
Description: Договорились.
|
||||
Approved: Авторизация устройства одобрена. Теперь вы можете вернуться к устройству.
|
||||
Denied: Отказано в авторизации устройства. Теперь вы можете вернуться к устройству.
|
||||
|
||||
Footer:
|
||||
PoweredBy: Руководствовался
|
||||
Tos: ТОТ
|
||||
PrivacyPolicy: Политика конфиденциальности
|
||||
Help: Справка
|
||||
SupportEmail: Электронная почта службы поддержки
|
||||
|
||||
SignIn: Вход с помощью {{.Provider}}
|
||||
|
||||
Errors:
|
||||
Internal: Произошла внутренняя ошибка
|
||||
AuthRequest:
|
||||
NotFound: Не удалось найти authrequest
|
||||
UserAgentNotCorresponding: User Agent не соответствует
|
||||
UserAgentNotFound: Идентификатор агента пользователя не найден
|
||||
TokenNotFound: Токен не найден
|
||||
RequestTypeNotSupported: Тип запроса не поддерживается
|
||||
MissingParameters: Отсутствуют обязательные параметры
|
||||
User:
|
||||
NotFound: Не удалось найти пользователя
|
||||
AlreadyExists: Пользователь уже существует
|
||||
Inactive: Пользователь неактивен
|
||||
NotFoundOnOrg: Не удалось найти пользователя в выбранной организации
|
||||
NotAllowedOrg: Пользователь не является членом требуемой организации
|
||||
NotMatchingUserID: Пользователь и пользователь в authrequest не совпадают
|
||||
UserIDMissing: UserID пуст
|
||||
Invalid: Неверные пользовательские данные
|
||||
DomainNotAllowedAsUsername: Домен уже зарезервирован и не может быть использован
|
||||
NotAllowedToLink: Пользователю не разрешается связываться с внешним провайдером входа в систему
|
||||
Profile:
|
||||
NotFound: Профиль не найден
|
||||
NotChanged: Профиль не изменен
|
||||
Empty: Профиль пуст
|
||||
FirstNameEmpty: Имя в профиле пусто
|
||||
LastNameEmpty: Фамилия в профиле пуста
|
||||
IDMissing: Отсутствует идентификатор профиля
|
||||
Email:
|
||||
NotFound: Электронная почта не найдена
|
||||
Invalid: Адрес электронной почты недействителен
|
||||
AlreadyVerified: Электронная почта уже подтверждена
|
||||
NotChanged: Адрес электронной почты не изменился
|
||||
Empty: Электронная почта пуста
|
||||
IDMissing: Отсутствует идентификатор электронной почты
|
||||
Phone:
|
||||
NotFound: Телефон не найден
|
||||
Invalid: Телефон недействителен
|
||||
AlreadyVerified: Телефон уже проверен
|
||||
Empty: Телефон пуст
|
||||
NotChanged: Телефон не менялся
|
||||
Address:
|
||||
NotFound: Адрес не найден
|
||||
NotChanged: Адрес не изменился
|
||||
Username:
|
||||
AlreadyExists: Имя пользователя уже занято
|
||||
Reserved: Имя пользователя уже занято
|
||||
Empty: Имя пользователя пусто
|
||||
Password:
|
||||
ConfirmationWrong: Неверное подтверждение пароля
|
||||
Empty: Пароль пуст
|
||||
Invalid: Пароль недействителен
|
||||
InvalidAndLocked: Пароль недействителен и пользователь заблокирован, обратитесь к администратору.
|
||||
NotChanged: Пароль не изменен
|
||||
UsernameOrPassword:
|
||||
Invalid: Имя пользователя или пароль недействительны
|
||||
PasswordComplexityPolicy:
|
||||
NotFound: Политика паролей не найдена
|
||||
MinLength: Пароль слишком короткий
|
||||
HasLower: Пароль должен содержать строчную букву
|
||||
HasUpper: Пароль должен содержать верхнюю букву
|
||||
HasNumber: Пароль должен содержать цифру
|
||||
HasSymbol: Пароль должен содержать символ
|
||||
Code:
|
||||
Expired: Срок действия кода истек
|
||||
Invalid: Код недействителен
|
||||
Empty: Код пустой
|
||||
CryptoCodeNil: Криптокод равен нулю
|
||||
NotFound: Не удалось найти код
|
||||
GeneratorAlgNotSupported: Неподдерживаемый алгоритм генератора
|
||||
EmailVerify:
|
||||
UserIDEmpty: UserID пуст
|
||||
ExternalData:
|
||||
CouldNotRead: Внешние данные не могут быть прочитаны правильно
|
||||
MFA:
|
||||
NoProviders: Нет доступных многофакторных провайдеров
|
||||
OTP:
|
||||
AlreadyReady: Одноразовый код-пароль уже настроен
|
||||
NotExisting: Одноразовый код-пароль не настроен
|
||||
InvalidCode: Неверный код-пароль
|
||||
NotReady: Одноразовый код-пароль не готов
|
||||
Locked: Пользователь заблокирован
|
||||
SomethingWentWrong: Что-то пошло не так
|
||||
NotActive: Пользователь не активен
|
||||
ExternalIDP:
|
||||
IDPTypeNotImplemented: Тип IDP не реализован
|
||||
NotAllowed: Внешний поставщик входа в систему не допускается
|
||||
IDPConfigIDEmpty: Идентификатор поставщика удостоверений пуст
|
||||
ExternalUserIDEmpty: Идентификатор внешнего пользователя пуст
|
||||
UserDisplayNameEmpty: Отображаемое имя пользователя пусто
|
||||
NoExternalUserData: Внешние пользовательские данные не получены
|
||||
CreationNotAllowed: Создание нового пользователя не допускается на этом провайдере
|
||||
LinkingNotAllowed: Привязка пользователя к этому провайдеру запрещена
|
||||
GrantRequired: Вход в систему невозможен. Пользователь должен иметь хотя бы одно разрешение в рамках приложения. Пожалуйста, свяжитесь с вашим администратором.
|
||||
ProjectRequired: Вход в систему невозможен. Организация пользователя должна быть предоставлена проекту. Обратитесь к администратору.
|
||||
IdentityProvider:
|
||||
InvalidConfig: Недопустимая конфигурация поставщика удостоверений
|
||||
IAM:
|
||||
LockoutPolicy:
|
||||
NotExisting: Политика блокировки не существует
|
||||
Org:
|
||||
LoginPolicy:
|
||||
RegistrationNotAllowed: Регистрация не допускается
|
||||
DeviceAuth:
|
||||
NotExisting: Код пользователя не существует
|
||||
|
||||
optional: (необязательно)
|
||||
@@ -246,6 +246,8 @@ RegistrationUser:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
GenderLabel: 性别
|
||||
Female: 女性
|
||||
Male: 男性
|
||||
@@ -282,6 +284,8 @@ ExternalRegistrationUserOverview:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
TosAndPrivacyLabel: 条款和条款
|
||||
TosConfirm: 我接受
|
||||
TosLinkText: 服务条款
|
||||
@@ -346,7 +350,8 @@ ExternalNotFound:
|
||||
Bulgarian: Български
|
||||
Portuguese: Português
|
||||
Macedonian: Македонски
|
||||
|
||||
Czech: Čeština
|
||||
Russian: Русский
|
||||
DeviceAuth:
|
||||
Title: 设备授权
|
||||
UserCode:
|
||||
|
||||
@@ -86,6 +86,10 @@
|
||||
</option>
|
||||
<option value="mk" id="mk" {{if (selectedLanguage "mk")}} selected {{end}}>{{t "ExternalNotFound.Macedonian"}}
|
||||
</option>
|
||||
<option value="cs" id="cs" {{if (selectedLanguage "cs")}} selected {{end}}>{{t "ExternalNotFound.Czech"}}
|
||||
</option>
|
||||
<option value="ru" id="mk" {{if (selectedLanguage "ru")}} selected {{end}}>{{t "ExternalNotFound.Russian"}}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user