Rewrite identity and access management
Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -22,85 +22,84 @@ import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
"github.com/99designs/gqlgen/graphql/handler"
|
||||
"github.com/99designs/gqlgen/graphql/handler/extension"
|
||||
"github.com/99designs/gqlgen/graphql/handler/transport"
|
||||
"github.com/99designs/gqlgen/graphql/playground"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/vektah/gqlparser/v2/ast"
|
||||
"github.com/vektah/gqlparser/v2/gqlerror"
|
||||
"go.gearno.de/crypto/uuid"
|
||||
"go.gearno.de/kit/httpserver"
|
||||
"go.gearno.de/kit/log"
|
||||
"go.probo.inc/probo/pkg/auth"
|
||||
"go.probo.inc/probo/pkg/authz"
|
||||
"go.probo.inc/probo/pkg/baseurl"
|
||||
"go.probo.inc/probo/pkg/connector"
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/iam"
|
||||
"go.probo.inc/probo/pkg/probo"
|
||||
"go.probo.inc/probo/pkg/saferedirect"
|
||||
"go.probo.inc/probo/pkg/securecookie"
|
||||
connect_v1 "go.probo.inc/probo/pkg/server/api/connect/v1"
|
||||
"go.probo.inc/probo/pkg/server/api/console/v1/schema"
|
||||
serverauth "go.probo.inc/probo/pkg/server/auth"
|
||||
"go.probo.inc/probo/pkg/server/gqlutils"
|
||||
"go.probo.inc/probo/pkg/server/session"
|
||||
"go.probo.inc/probo/pkg/statelesstoken"
|
||||
)
|
||||
|
||||
type (
|
||||
AuthConfig struct {
|
||||
CookieName string
|
||||
CookieDomain string
|
||||
SessionDuration time.Duration
|
||||
CookieSecret string
|
||||
CookieSecure bool
|
||||
}
|
||||
|
||||
Resolver struct {
|
||||
proboSvc *probo.Service
|
||||
authSvc *auth.Service
|
||||
authzSvc *authz.Service
|
||||
samlSvc *auth.SAMLService
|
||||
authCfg AuthConfig
|
||||
probo *probo.Service
|
||||
iam *iam.Service
|
||||
customDomainCname string
|
||||
schema *ast.Schema
|
||||
}
|
||||
)
|
||||
|
||||
func ensureAuthenticated(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
|
||||
identity := connect_v1.UserFromContext(ctx)
|
||||
|
||||
if identity == nil {
|
||||
return func(ctx context.Context) *graphql.Response {
|
||||
return &graphql.Response{
|
||||
Errors: gqlerror.List{
|
||||
gqlutils.Unauthorized(),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctxKey struct{ name string }
|
||||
)
|
||||
|
||||
var (
|
||||
sessionContextKey = &ctxKey{name: "session"}
|
||||
)
|
||||
|
||||
func SessionFromContext(ctx context.Context) *coredata.Session {
|
||||
session, _ := ctx.Value(sessionContextKey).(*coredata.Session)
|
||||
return session
|
||||
}
|
||||
|
||||
func UserFromContext(ctx context.Context) *coredata.User {
|
||||
return serverauth.UserFromContext(ctx)
|
||||
}
|
||||
|
||||
func UserAPIKeyFromContext(ctx context.Context) *coredata.UserAPIKey {
|
||||
return serverauth.UserAPIKeyFromContext(ctx)
|
||||
return next(ctx)
|
||||
}
|
||||
|
||||
func NewMux(
|
||||
logger *log.Logger,
|
||||
proboSvc *probo.Service,
|
||||
authSvc *auth.Service,
|
||||
authzSvc *authz.Service,
|
||||
authCfg AuthConfig,
|
||||
iamSvc *iam.Service,
|
||||
cookieConfig securecookie.Config,
|
||||
tokenSecret string,
|
||||
connectorRegistry *connector.ConnectorRegistry,
|
||||
safeRedirect *saferedirect.SafeRedirect,
|
||||
baseURL *baseurl.BaseURL,
|
||||
customDomainCname string,
|
||||
samlSvc *auth.SAMLService,
|
||||
) *chi.Mux {
|
||||
r := chi.NewMux()
|
||||
|
||||
safeRedirect := &saferedirect.SafeRedirect{AllowedHost: baseURL.Host()}
|
||||
|
||||
sessionMiddleware := connect_v1.NewSessionMiddleware(iamSvc, cookieConfig)
|
||||
apiKeyMiddleware := connect_v1.NewAPIKeyMiddleware(iamSvc)
|
||||
|
||||
r.Use(sessionMiddleware)
|
||||
r.Use(apiKeyMiddleware)
|
||||
|
||||
config := schema.Config{
|
||||
Resolvers: &Resolver{
|
||||
probo: proboSvc,
|
||||
iam: iamSvc,
|
||||
customDomainCname: customDomainCname,
|
||||
},
|
||||
}
|
||||
es := schema.NewExecutableSchema(config)
|
||||
h := gqlutils.NewHandler(es, logger)
|
||||
h.AroundOperations(ensureAuthenticated)
|
||||
|
||||
r.Handle("/graphql", h)
|
||||
|
||||
r.Get(
|
||||
"/documents/signing-requests",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -111,7 +110,7 @@ func NewMux(
|
||||
}
|
||||
|
||||
token = strings.TrimPrefix(token, "Bearer ")
|
||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](authCfg.CookieSecret, probo.TokenTypeSigningRequest, token)
|
||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](tokenSecret, probo.TokenTypeSigningRequest, token)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
@@ -139,7 +138,7 @@ func NewMux(
|
||||
return
|
||||
}
|
||||
|
||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](authCfg.CookieSecret, probo.TokenTypeSigningRequest, token)
|
||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](tokenSecret, probo.TokenTypeSigningRequest, token)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
@@ -194,7 +193,7 @@ func NewMux(
|
||||
}
|
||||
|
||||
token = strings.TrimPrefix(token, "Bearer ")
|
||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](authCfg.CookieSecret, probo.TokenTypeSigningRequest, token)
|
||||
data, err := statelesstoken.ValidateToken[probo.SigningRequestData](tokenSecret, probo.TokenTypeSigningRequest, token)
|
||||
if err != nil {
|
||||
http.Error(w, "invalid token", http.StatusUnauthorized)
|
||||
return
|
||||
@@ -217,7 +216,7 @@ func NewMux(
|
||||
},
|
||||
)
|
||||
|
||||
r.Get("/connectors/initiate", WithSession(authSvc, authzSvc, authCfg, func(w http.ResponseWriter, r *http.Request) {
|
||||
r.Get("/connectors/initiate", func(w http.ResponseWriter, r *http.Request) {
|
||||
provider := r.URL.Query().Get("provider")
|
||||
if provider != "SLACK" {
|
||||
httpserver.RenderError(w, http.StatusBadRequest, fmt.Errorf("unsupported provider"))
|
||||
@@ -229,7 +228,23 @@ func NewMux(
|
||||
panic(fmt.Errorf("cannot parse organization id: %w", err))
|
||||
}
|
||||
|
||||
_ = GetTenantService(r.Context(), proboSvc, organizationID.TenantID())
|
||||
identity := connect_v1.UserFromContext(r.Context())
|
||||
apiKey := connect_v1.APIKeyFromContext(r.Context())
|
||||
if identity == nil {
|
||||
httpserver.RenderError(w, http.StatusUnauthorized, fmt.Errorf("authentication required"))
|
||||
return
|
||||
}
|
||||
|
||||
var credentialID *gid.GID
|
||||
if apiKey != nil {
|
||||
credentialID = &apiKey.ID
|
||||
}
|
||||
|
||||
// Ensure the actor (and optional API key) can access this organization.
|
||||
if err := iamSvc.AccessManagementService.Authorize(r.Context(), identity.ID, credentialID, organizationID, iam.ActionGet); err != nil {
|
||||
httpserver.RenderError(w, http.StatusForbidden, err)
|
||||
return
|
||||
}
|
||||
|
||||
redirectURL, err := connectorRegistry.Initiate(r.Context(), provider, organizationID, r)
|
||||
if err != nil {
|
||||
@@ -239,7 +254,7 @@ func NewMux(
|
||||
// Allow external redirects for Slack OAuth only for now
|
||||
slackSafeRedirect := &saferedirect.SafeRedirect{AllowedHost: "slack.com"}
|
||||
slackSafeRedirect.Redirect(w, r, redirectURL, "/", http.StatusSeeOther)
|
||||
}))
|
||||
})
|
||||
|
||||
r.Get("/connectors/complete", func(w http.ResponseWriter, r *http.Request) {
|
||||
provider := r.URL.Query().Get("provider")
|
||||
@@ -288,146 +303,16 @@ func NewMux(
|
||||
if continueURL != "" {
|
||||
safeRedirect.Redirect(w, r, continueURL, "/", http.StatusSeeOther)
|
||||
} else {
|
||||
redirectURL := fmt.Sprintf("/organizations/%s", organizationID.String())
|
||||
redirectURL := baseURL.WithPath("/organizations/" + organizationID.String()).MustString()
|
||||
safeRedirect.Redirect(w, r, redirectURL, "/", http.StatusSeeOther)
|
||||
}
|
||||
})
|
||||
|
||||
r.Get("/", playground.Handler("GraphQL", "/api/console/v1/query"))
|
||||
r.Post("/query", graphqlHandler(logger, proboSvc, authSvc, authzSvc, samlSvc, authCfg, customDomainCname))
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// GetSchema returns the parsed GraphQL schema for the console API
|
||||
// This is used by other services like authz to extract permissions from @mustBeAuthorized directives
|
||||
func GetSchema() *ast.Schema {
|
||||
execSchema := schema.NewExecutableSchema(schema.Config{})
|
||||
return execSchema.Schema()
|
||||
}
|
||||
|
||||
func graphqlHandler(logger *log.Logger, proboSvc *probo.Service, authSvc *auth.Service, authzSvc *authz.Service, samlSvc *auth.SAMLService, authCfg AuthConfig, customDomainCname string) http.HandlerFunc {
|
||||
var mb int64 = 1 << 20
|
||||
|
||||
// Parse the schema first to make it available to resolvers
|
||||
execSchema := schema.NewExecutableSchema(schema.Config{})
|
||||
|
||||
cfg := schema.Config{
|
||||
Resolvers: &Resolver{
|
||||
proboSvc: proboSvc,
|
||||
authSvc: authSvc,
|
||||
authzSvc: authzSvc,
|
||||
samlSvc: samlSvc,
|
||||
authCfg: authCfg,
|
||||
customDomainCname: customDomainCname,
|
||||
schema: execSchema.Schema(),
|
||||
},
|
||||
}
|
||||
|
||||
es := schema.NewExecutableSchema(cfg)
|
||||
srv := handler.New(es)
|
||||
srv.AddTransport(transport.POST{})
|
||||
srv.AddTransport(
|
||||
transport.MultipartForm{
|
||||
MaxMemory: 32 * mb,
|
||||
MaxUploadSize: 50 * mb,
|
||||
},
|
||||
)
|
||||
srv.Use(extension.Introspection{})
|
||||
srv.Use(gqlutils.NewTracingExtension(logger))
|
||||
srv.SetRecoverFunc(gqlutils.RecoverFunc)
|
||||
|
||||
srv.AroundOperations(
|
||||
func(ctx context.Context, next graphql.OperationHandler) graphql.ResponseHandler {
|
||||
user := UserFromContext(ctx)
|
||||
|
||||
if user == nil {
|
||||
return func(ctx context.Context) *graphql.Response {
|
||||
return &graphql.Response{
|
||||
Errors: gqlerror.List{
|
||||
&gqlerror.Error{
|
||||
Message: "authentication required",
|
||||
Extensions: map[string]any{
|
||||
"code": "UNAUTHENTICATED",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next(ctx)
|
||||
},
|
||||
)
|
||||
|
||||
return WithSession(authSvc, authzSvc, authCfg, srv.ServeHTTP)
|
||||
}
|
||||
|
||||
func WithSession(authSvc *auth.Service, authzSvc *authz.Service, authCfg AuthConfig, next http.HandlerFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
if authCtx := serverauth.AuthenticateWithAPIKey(ctx, r, authSvc, authzSvc); authCtx != nil {
|
||||
next(w, r.WithContext(authCtx))
|
||||
return
|
||||
}
|
||||
|
||||
sessionAuthCfg := session.AuthConfig{
|
||||
CookieName: authCfg.CookieName,
|
||||
CookieSecret: authCfg.CookieSecret,
|
||||
CookieSecure: authCfg.CookieSecure,
|
||||
}
|
||||
|
||||
errorHandler := session.ErrorHandler{
|
||||
OnCookieError: func(err error) {
|
||||
panic(fmt.Errorf("cannot get session: %w", err))
|
||||
},
|
||||
OnParseError: func(w http.ResponseWriter, authCfg session.AuthConfig) {
|
||||
session.ClearCookie(w, authCfg)
|
||||
},
|
||||
OnSessionError: func(w http.ResponseWriter, authCfg session.AuthConfig) {
|
||||
session.ClearCookie(w, authCfg)
|
||||
},
|
||||
OnUserError: func(w http.ResponseWriter, authCfg session.AuthConfig) {
|
||||
session.ClearCookie(w, authCfg)
|
||||
},
|
||||
OnTenantError: func(err error) {
|
||||
panic(fmt.Errorf("cannot list tenants for user: %w", err))
|
||||
},
|
||||
}
|
||||
|
||||
authResult := session.TryAuth(ctx, w, r, authSvc, authzSvc, sessionAuthCfg, errorHandler)
|
||||
if authResult == nil {
|
||||
next(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
ctx = context.WithValue(ctx, sessionContextKey, authResult.Session)
|
||||
ctx = context.WithValue(ctx, serverauth.UserContextKey, authResult.User)
|
||||
ctx = context.WithValue(ctx, serverauth.UserTenantContextKey, &serverauth.UserTenantAccess{
|
||||
TenantIDs: authResult.TenantIDs,
|
||||
AuthErrors: authResult.AuthErrors,
|
||||
})
|
||||
|
||||
next(w, r.WithContext(ctx))
|
||||
|
||||
// Update session after the handler completes
|
||||
if _, err := authSvc.UpdateSession(ctx, authResult.Session.ID); err != nil {
|
||||
panic(fmt.Errorf("cannot update session: %w", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resolver) ProboService(ctx context.Context, tenantID gid.TenantID) *probo.TenantService {
|
||||
return GetTenantService(ctx, r.proboSvc, tenantID)
|
||||
}
|
||||
|
||||
func (r *Resolver) AuthzService(ctx context.Context, tenantID gid.TenantID) *authz.TenantAuthzService {
|
||||
return GetTenantAuthzService(ctx, r.authzSvc, tenantID)
|
||||
}
|
||||
|
||||
func (r *Resolver) AuthService(ctx context.Context, tenantID gid.TenantID) *auth.TenantAuthService {
|
||||
return GetTenantAuthService(ctx, r.authSvc, tenantID)
|
||||
return GetTenantService(ctx, r.probo, tenantID)
|
||||
}
|
||||
|
||||
func UnwrapOmittable[T any](field graphql.Omittable[T]) *T {
|
||||
@@ -439,26 +324,19 @@ func UnwrapOmittable[T any](field graphql.Omittable[T]) *T {
|
||||
}
|
||||
|
||||
func GetTenantService(ctx context.Context, proboSvc *probo.Service, tenantID gid.TenantID) *probo.TenantService {
|
||||
serverauth.RequireTenantAccess(ctx, tenantID)
|
||||
return proboSvc.WithTenant(tenantID)
|
||||
}
|
||||
|
||||
func GetTenantAuthzService(ctx context.Context, authzSvc *authz.Service, tenantID gid.TenantID) *authz.TenantAuthzService {
|
||||
serverauth.RequireTenantAccess(ctx, tenantID)
|
||||
return authzSvc.WithTenant(tenantID)
|
||||
}
|
||||
func (r *Resolver) MustBeAuthorized(ctx context.Context, entityID gid.GID, action iam.Action) {
|
||||
user := connect_v1.UserFromContext(ctx)
|
||||
apiKey := connect_v1.APIKeyFromContext(ctx)
|
||||
|
||||
func GetTenantAuthService(ctx context.Context, authSvc *auth.Service, tenantID gid.TenantID) *auth.TenantAuthService {
|
||||
serverauth.RequireTenantAccess(ctx, tenantID)
|
||||
return authSvc.WithTenant(tenantID)
|
||||
}
|
||||
var credentialID *gid.GID
|
||||
if apiKey != nil {
|
||||
credentialID = &apiKey.ID
|
||||
}
|
||||
|
||||
func (r *Resolver) MustBeAuthorized(ctx context.Context, entityID gid.GID, action authz.Action) {
|
||||
user := UserFromContext(ctx)
|
||||
apiKey := UserAPIKeyFromContext(ctx)
|
||||
|
||||
authzSvc := r.AuthzService(ctx, entityID.TenantID())
|
||||
err := authzSvc.Authorize(ctx, user, apiKey, entityID, action)
|
||||
err := r.iam.AccessManagementService.Authorize(ctx, user.ID, credentialID, entityID, action)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -34,15 +34,6 @@ type PageInfo {
|
||||
endCursor: CursorKey
|
||||
}
|
||||
|
||||
# Roles
|
||||
enum Role {
|
||||
OWNER
|
||||
ADMIN
|
||||
VIEWER
|
||||
AUDITOR
|
||||
FULL
|
||||
}
|
||||
|
||||
# Enums
|
||||
enum OrderDirection
|
||||
@goModel(model: "go.probo.inc/probo/pkg/page.OrderDirection") {
|
||||
@@ -83,31 +74,6 @@ enum PeopleKind @goModel(model: "go.probo.inc/probo/pkg/coredata.PeopleKind") {
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.PeopleKindServiceAccount")
|
||||
}
|
||||
|
||||
enum InvitationStatus
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.InvitationStatus") {
|
||||
PENDING
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationStatusPending")
|
||||
ACCEPTED
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationStatusAccepted")
|
||||
EXPIRED
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationStatusExpired")
|
||||
}
|
||||
|
||||
enum MembershipRole
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.MembershipRole") {
|
||||
OWNER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleOwner")
|
||||
ADMIN @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAdmin")
|
||||
EMPLOYEE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleEmployee")
|
||||
VIEWER @goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleViewer")
|
||||
AUDITOR
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipRoleAuditor")
|
||||
}
|
||||
|
||||
enum APIRole @goModel(model: "go.probo.inc/probo/pkg/coredata.APIRole") {
|
||||
FULL @goEnum(value: "go.probo.inc/probo/pkg/coredata.APIRoleFull")
|
||||
}
|
||||
|
||||
enum DocumentStatus
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.DocumentStatus") {
|
||||
DRAFT @goEnum(value: "go.probo.inc/probo/pkg/coredata.DocumentStatusDraft")
|
||||
@@ -143,26 +109,6 @@ enum AuditState @goModel(model: "go.probo.inc/probo/pkg/coredata.AuditState") {
|
||||
OUTDATED @goEnum(value: "go.probo.inc/probo/pkg/coredata.AuditStateOutdated")
|
||||
}
|
||||
|
||||
enum SAMLEnforcementPolicy
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicy") {
|
||||
OFF @goEnum(value: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicyOff")
|
||||
OPTIONAL
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicyOptional"
|
||||
)
|
||||
REQUIRED
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.SAMLEnforcementPolicyRequired"
|
||||
)
|
||||
}
|
||||
|
||||
enum UserAuthMethod
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.UserAuthMethod") {
|
||||
PASSWORD
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.UserAuthMethodPassword")
|
||||
SAML @goEnum(value: "go.probo.inc/probo/pkg/coredata.UserAuthMethodSAML")
|
||||
}
|
||||
|
||||
enum TrustCenterVisibility
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.TrustCenterVisibility") {
|
||||
NONE
|
||||
@@ -226,8 +172,7 @@ enum ObligationStatus
|
||||
|
||||
enum ObligationType
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.ObligationType") {
|
||||
LEGAL
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ObligationTypeLegal")
|
||||
LEGAL @goEnum(value: "go.probo.inc/probo/pkg/coredata.ObligationTypeLegal")
|
||||
CONTRACTUAL
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.ObligationTypeContractual")
|
||||
}
|
||||
@@ -269,17 +214,11 @@ enum ContinualImprovementPriority
|
||||
}
|
||||
|
||||
enum RightsRequestType
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.RightsRequestType"
|
||||
) {
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestType") {
|
||||
ACCESS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeAccess"
|
||||
)
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeAccess")
|
||||
DELETION
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeDeletion"
|
||||
)
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypeDeletion")
|
||||
PORTABILITY
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestTypePortability"
|
||||
@@ -287,21 +226,13 @@ enum RightsRequestType
|
||||
}
|
||||
|
||||
enum RightsRequestState
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.RightsRequestState"
|
||||
) {
|
||||
TODO
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateTodo"
|
||||
)
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestState") {
|
||||
TODO @goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateTodo")
|
||||
IN_PROGRESS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateInProgress"
|
||||
)
|
||||
DONE
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateDone"
|
||||
)
|
||||
DONE @goEnum(value: "go.probo.inc/probo/pkg/coredata.RightsRequestStateDone")
|
||||
}
|
||||
|
||||
enum ProcessingActivitySpecialOrCriminalDatum
|
||||
@@ -429,9 +360,7 @@ enum DataProtectionImpactAssessmentResidualRisk
|
||||
}
|
||||
|
||||
enum ProcessingActivityRole
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityRole"
|
||||
) {
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.ProcessingActivityRole") {
|
||||
CONTROLLER
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.ProcessingActivityRoleController"
|
||||
@@ -443,12 +372,6 @@ enum ProcessingActivityRole
|
||||
}
|
||||
|
||||
# Order Field Enums
|
||||
enum UserOrderField
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.UserOrderField") {
|
||||
CREATED_AT
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.UserOrderFieldCreatedAt")
|
||||
}
|
||||
|
||||
enum PeopleOrderField
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.PeopleOrderField") {
|
||||
FULL_NAME
|
||||
@@ -1110,9 +1033,7 @@ enum ContinualImprovementOrderField
|
||||
}
|
||||
|
||||
enum RightsRequestOrderField
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderField"
|
||||
) {
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderField") {
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.RightsRequestOrderFieldCreatedAt"
|
||||
@@ -1259,57 +1180,7 @@ enum SnapshotOrderField
|
||||
TYPE @goEnum(value: "go.probo.inc/probo/pkg/coredata.SnapshotOrderFieldType")
|
||||
}
|
||||
|
||||
enum MembershipOrderField
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.MembershipOrderField") {
|
||||
FULL_NAME
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.MembershipOrderFieldFullName"
|
||||
)
|
||||
EMAIL_ADDRESS
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.MembershipOrderFieldEmailAddress"
|
||||
)
|
||||
ROLE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.MembershipOrderFieldRole")
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.MembershipOrderFieldCreatedAt"
|
||||
)
|
||||
}
|
||||
|
||||
enum InvitationOrderField
|
||||
@goModel(model: "go.probo.inc/probo/pkg/coredata.InvitationOrderField") {
|
||||
FULL_NAME
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.InvitationOrderFieldFullName"
|
||||
)
|
||||
EMAIL
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationOrderFieldEmail")
|
||||
ROLE
|
||||
@goEnum(value: "go.probo.inc/probo/pkg/coredata.InvitationOrderFieldRole")
|
||||
CREATED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.InvitationOrderFieldCreatedAt"
|
||||
)
|
||||
EXPIRES_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.InvitationOrderFieldExpiresAt"
|
||||
)
|
||||
ACCEPTED_AT
|
||||
@goEnum(
|
||||
value: "go.probo.inc/probo/pkg/coredata.InvitationOrderFieldAcceptedAt"
|
||||
)
|
||||
}
|
||||
|
||||
# Input Types
|
||||
input UserOrder
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.UserOrderBy"
|
||||
) {
|
||||
direction: OrderDirection!
|
||||
field: UserOrderField!
|
||||
}
|
||||
|
||||
input PeopleOrder
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.PeopleOrderBy"
|
||||
@@ -1531,23 +1402,6 @@ input SnapshotOrder
|
||||
field: SnapshotOrderField!
|
||||
}
|
||||
|
||||
input MembershipOrder
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MembershipOrderBy"
|
||||
) {
|
||||
direction: OrderDirection!
|
||||
field: MembershipOrderField!
|
||||
}
|
||||
|
||||
input InvitationOrder {
|
||||
direction: OrderDirection!
|
||||
field: InvitationOrderField!
|
||||
}
|
||||
|
||||
input InvitationFilter {
|
||||
statuses: [InvitationStatus!]
|
||||
}
|
||||
|
||||
input DocumentVersionFilter {
|
||||
status: DocumentStatus
|
||||
}
|
||||
@@ -1595,7 +1449,6 @@ input ContinualImprovementFilter {
|
||||
snapshotId: ID
|
||||
}
|
||||
|
||||
|
||||
input ProcessingActivityFilter {
|
||||
snapshotId: ID
|
||||
}
|
||||
@@ -1655,23 +1508,6 @@ type Organization implements Node {
|
||||
headquarterAddress: String
|
||||
context: OrganizationContext @goField(forceResolver: true)
|
||||
|
||||
memberships(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: MembershipOrder
|
||||
): MembershipConnection! @goField(forceResolver: true)
|
||||
|
||||
invitations(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: InvitationOrder
|
||||
filter: InvitationFilter
|
||||
): InvitationConnection! @goField(forceResolver: true)
|
||||
|
||||
slackConnections(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
@@ -1874,44 +1710,10 @@ type Organization implements Node {
|
||||
|
||||
customDomain: CustomDomain @goField(forceResolver: true)
|
||||
|
||||
samlConfigurations: [SAMLConfiguration!]! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type User implements Node {
|
||||
id: ID!
|
||||
fullName: String!
|
||||
email: EmailAddr!
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type Membership implements Node {
|
||||
id: ID!
|
||||
userID: ID!
|
||||
organizationID: ID!
|
||||
role: MembershipRole!
|
||||
fullName: String!
|
||||
emailAddress: EmailAddr!
|
||||
authMethod: UserAuthMethod! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type Invitation implements Node {
|
||||
id: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
role: MembershipRole!
|
||||
status: InvitationStatus!
|
||||
expiresAt: Datetime!
|
||||
acceptedAt: Datetime
|
||||
createdAt: Datetime!
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
type SlackConnection {
|
||||
id: ID!
|
||||
channel: String
|
||||
@@ -2324,7 +2126,8 @@ type StateOfApplicability implements Node {
|
||||
orderBy: ControlOrder
|
||||
filter: ControlFilter
|
||||
): ControlConnection! @goField(forceResolver: true)
|
||||
availableControls: [AvailableStateOfApplicabilityControl!]! @goField(forceResolver: true)
|
||||
availableControls: [AvailableStateOfApplicabilityControl!]!
|
||||
@goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -2510,8 +2313,10 @@ type ProcessingActivity implements Node {
|
||||
before: CursorKey
|
||||
orderBy: VendorOrder
|
||||
): VendorConnection! @goField(forceResolver: true)
|
||||
dataProtectionImpactAssessment: DataProtectionImpactAssessment @goField(forceResolver: true)
|
||||
transferImpactAssessment: TransferImpactAssessment @goField(forceResolver: true)
|
||||
dataProtectionImpactAssessment: DataProtectionImpactAssessment
|
||||
@goField(forceResolver: true)
|
||||
transferImpactAssessment: TransferImpactAssessment
|
||||
@goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
@@ -2580,15 +2385,6 @@ type Session {
|
||||
|
||||
type Viewer {
|
||||
id: ID!
|
||||
user: User!
|
||||
|
||||
organizations(
|
||||
first: Int
|
||||
after: CursorKey
|
||||
last: Int
|
||||
before: CursorKey
|
||||
orderBy: OrganizationOrder
|
||||
): OrganizationConnection! @goField(forceResolver: true)
|
||||
|
||||
signableDocuments(
|
||||
organizationId: ID!
|
||||
@@ -2602,17 +2398,6 @@ type Viewer {
|
||||
signableDocument(id: ID!): SignableDocument @goField(forceResolver: true)
|
||||
}
|
||||
|
||||
# Connection Types
|
||||
type OrganizationConnection {
|
||||
edges: [OrganizationEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type OrganizationEdge {
|
||||
cursor: CursorKey!
|
||||
node: Organization!
|
||||
}
|
||||
|
||||
type TrustCenterConnection {
|
||||
edges: [TrustCenterEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
@@ -2729,34 +2514,6 @@ type TrustCenterFileEdge {
|
||||
node: TrustCenterFile!
|
||||
}
|
||||
|
||||
type UserConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.UserConnection"
|
||||
) {
|
||||
totalCount: Int! @goField(forceResolver: true)
|
||||
edges: [UserEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type MembershipConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.MembershipConnection"
|
||||
) {
|
||||
totalCount: Int! @goField(forceResolver: true)
|
||||
edges: [MembershipEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type MembershipEdge {
|
||||
cursor: CursorKey!
|
||||
node: Membership!
|
||||
}
|
||||
|
||||
type UserEdge {
|
||||
cursor: CursorKey!
|
||||
node: User!
|
||||
}
|
||||
|
||||
type PeopleConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.PeopleConnection"
|
||||
@@ -3126,20 +2883,6 @@ type File {
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
type InvitationConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.InvitationConnection"
|
||||
) {
|
||||
totalCount: Int! @goField(forceResolver: true)
|
||||
edges: [InvitationEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type InvitationEdge {
|
||||
cursor: CursorKey!
|
||||
node: Invitation!
|
||||
}
|
||||
|
||||
# Root Types
|
||||
type Query {
|
||||
node(id: ID!): Node!
|
||||
@@ -3147,22 +2890,9 @@ type Query {
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
# Organization mutations
|
||||
createOrganization(
|
||||
input: CreateOrganizationInput!
|
||||
): CreateOrganizationPayload!
|
||||
updateOrganization(
|
||||
input: UpdateOrganizationInput!
|
||||
): UpdateOrganizationPayload!
|
||||
updateOrganizationContext(
|
||||
input: UpdateOrganizationContextInput!
|
||||
): UpdateOrganizationContextPayload!
|
||||
deleteOrganizationHorizontalLogo(
|
||||
input: DeleteOrganizationHorizontalLogoInput!
|
||||
): DeleteOrganizationHorizontalLogoPayload!
|
||||
deleteOrganization(
|
||||
input: DeleteOrganizationInput!
|
||||
): DeleteOrganizationPayload!
|
||||
updateTrustCenter(input: UpdateTrustCenterInput!): UpdateTrustCenterPayload!
|
||||
uploadTrustCenterNDA(
|
||||
input: UploadTrustCenterNDAInput!
|
||||
@@ -3203,13 +2933,7 @@ type Mutation {
|
||||
deleteTrustCenterFile(
|
||||
input: DeleteTrustCenterFileInput!
|
||||
): DeleteTrustCenterFilePayload!
|
||||
# User mutations
|
||||
confirmEmail(input: ConfirmEmailInput!): ConfirmEmailPayload!
|
||||
inviteUser(input: InviteUserInput!): InviteUserPayload!
|
||||
acceptInvitation(input: AcceptInvitationInput!): AcceptInvitationPayload!
|
||||
deleteInvitation(input: DeleteInvitationInput!): DeleteInvitationPayload!
|
||||
removeMember(input: RemoveMemberInput!): RemoveMemberPayload!
|
||||
updateMembership(input: UpdateMembershipInput!): UpdateMembershipPayload!
|
||||
|
||||
# People mutations
|
||||
createPeople(input: CreatePeopleInput!): CreatePeoplePayload!
|
||||
updatePeople(input: UpdatePeopleInput!): UpdatePeoplePayload!
|
||||
@@ -3407,6 +3131,7 @@ type Mutation {
|
||||
input: CancelSignatureRequestInput!
|
||||
): CancelSignatureRequestPayload!
|
||||
signDocument(input: SignDocumentInput!): SignDocumentPayload!
|
||||
|
||||
exportDocumentVersionPDF(
|
||||
input: ExportDocumentVersionPDFInput!
|
||||
): ExportDocumentVersionPDFPayload!
|
||||
@@ -3515,25 +3240,6 @@ type Mutation {
|
||||
deleteCustomDomain(
|
||||
input: DeleteCustomDomainInput!
|
||||
): DeleteCustomDomainPayload!
|
||||
# SAML Configuration mutations (OWNER/ADMIN only)
|
||||
# Step 1: Initiate domain verification (creates SAML config with unverified domain)
|
||||
initiateDomainVerification(
|
||||
input: InitiateDomainVerificationInput!
|
||||
): InitiateDomainVerificationPayload!
|
||||
# Step 2: Verify domain ownership via DNS TXT record
|
||||
verifyDomain(input: VerifyDomainInput!): VerifyDomainPayload!
|
||||
# Step 3: Configure SAML (only allowed after domain is verified)
|
||||
createSAMLConfiguration(
|
||||
input: CreateSAMLConfigurationInput!
|
||||
): CreateSAMLConfigurationPayload!
|
||||
updateSAMLConfiguration(
|
||||
input: UpdateSAMLConfigurationInput!
|
||||
): UpdateSAMLConfigurationPayload!
|
||||
deleteSAMLConfiguration(
|
||||
input: DeleteSAMLConfigurationInput!
|
||||
): DeleteSAMLConfigurationPayload!
|
||||
enableSAML(input: EnableSAMLInput!): EnableSAMLPayload!
|
||||
disableSAML(input: DisableSAMLInput!): DisableSAMLPayload!
|
||||
}
|
||||
|
||||
# Input Types
|
||||
@@ -3545,34 +3251,11 @@ type GenerateFrameworkStateOfApplicabilityPayload {
|
||||
data: String!
|
||||
}
|
||||
|
||||
input CreateOrganizationInput {
|
||||
name: String!
|
||||
}
|
||||
|
||||
input UpdateOrganizationInput {
|
||||
organizationId: ID!
|
||||
name: String
|
||||
description: String @goField(omittable: true)
|
||||
websiteUrl: String @goField(omittable: true)
|
||||
email: String @goField(omittable: true)
|
||||
headquarterAddress: String @goField(omittable: true)
|
||||
logoFile: Upload
|
||||
horizontalLogoFile: Upload
|
||||
}
|
||||
|
||||
input UpdateOrganizationContextInput {
|
||||
organizationId: ID!
|
||||
summary: String @goField(omittable: true)
|
||||
}
|
||||
|
||||
input DeleteOrganizationHorizontalLogoInput {
|
||||
organizationId: ID!
|
||||
}
|
||||
|
||||
input DeleteOrganizationInput {
|
||||
organizationId: ID!
|
||||
}
|
||||
|
||||
input UpdateTrustCenterInput {
|
||||
trustCenterId: ID!
|
||||
active: Boolean
|
||||
@@ -3763,7 +3446,7 @@ input UpdatePeopleInput {
|
||||
id: ID!
|
||||
fullName: String
|
||||
primaryEmailAddress: EmailAddr
|
||||
additionalEmailAddresses: [EmailAddr!]
|
||||
additionalEmailAddresses: [EmailAddr!] @goField(omittable: true)
|
||||
kind: PeopleKind
|
||||
position: String @goField(omittable: true)
|
||||
contractStartDate: Datetime @goField(omittable: true)
|
||||
@@ -3844,7 +3527,6 @@ input DeleteTaskInput {
|
||||
taskId: ID!
|
||||
}
|
||||
|
||||
|
||||
input CreateControlMeasureMappingInput {
|
||||
controlId: ID!
|
||||
measureId: ID!
|
||||
@@ -4152,7 +3834,6 @@ input DeleteStateOfApplicabilityInput {
|
||||
stateOfApplicabilityId: ID!
|
||||
}
|
||||
|
||||
|
||||
type StateOfApplicabilityControl {
|
||||
id: ID!
|
||||
stateOfApplicabilityId: ID!
|
||||
@@ -4162,13 +3843,19 @@ type StateOfApplicabilityControl {
|
||||
justification: String
|
||||
}
|
||||
|
||||
type StateOfApplicabilityControlConnection @goModel(model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StateOfApplicabilityControlConnection") {
|
||||
type StateOfApplicabilityControlConnection
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StateOfApplicabilityControlConnection"
|
||||
) {
|
||||
totalCount: Int!
|
||||
edges: [StateOfApplicabilityControlEdge!]!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type StateOfApplicabilityControlEdge @goModel(model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StateOfApplicabilityControlEdge") {
|
||||
type StateOfApplicabilityControlEdge
|
||||
@goModel(
|
||||
model: "go.probo.inc/probo/pkg/server/api/console/v1/types.StateOfApplicabilityControlEdge"
|
||||
) {
|
||||
cursor: CursorKey!
|
||||
node: StateOfApplicabilityControl!
|
||||
}
|
||||
@@ -4202,38 +3889,6 @@ enum StateOfApplicabilityOrderField
|
||||
value: "go.probo.inc/probo/pkg/coredata.StateOfApplicabilityOrderFieldCreatedAt"
|
||||
)
|
||||
}
|
||||
|
||||
input ConfirmEmailInput {
|
||||
token: String!
|
||||
}
|
||||
|
||||
input InviteUserInput {
|
||||
organizationId: ID!
|
||||
email: EmailAddr!
|
||||
fullName: String!
|
||||
role: MembershipRole!
|
||||
createPeople: Boolean!
|
||||
}
|
||||
|
||||
input AcceptInvitationInput {
|
||||
invitationId: ID!
|
||||
}
|
||||
|
||||
input DeleteInvitationInput {
|
||||
invitationId: ID!
|
||||
}
|
||||
|
||||
input RemoveMemberInput {
|
||||
organizationId: ID!
|
||||
memberId: ID!
|
||||
}
|
||||
|
||||
input UpdateMembershipInput {
|
||||
organizationId: ID!
|
||||
memberId: ID!
|
||||
role: MembershipRole!
|
||||
}
|
||||
|
||||
input CreateControlInput {
|
||||
frameworkId: ID!
|
||||
sectionTitle: String!
|
||||
@@ -4517,13 +4172,6 @@ input DeleteSnapshotInput {
|
||||
}
|
||||
|
||||
# Payload Types
|
||||
type CreateOrganizationPayload {
|
||||
organizationEdge: OrganizationEdge!
|
||||
}
|
||||
|
||||
type UpdateOrganizationPayload {
|
||||
organization: Organization!
|
||||
}
|
||||
|
||||
type UpdateOrganizationContextPayload {
|
||||
context: OrganizationContext!
|
||||
@@ -4534,14 +4182,6 @@ type OrganizationContext {
|
||||
summary: String
|
||||
}
|
||||
|
||||
type DeleteOrganizationHorizontalLogoPayload {
|
||||
organization: Organization!
|
||||
}
|
||||
|
||||
type DeleteOrganizationPayload {
|
||||
deletedOrganizationId: ID!
|
||||
}
|
||||
|
||||
type UpdateTrustCenterPayload {
|
||||
trustCenter: TrustCenter!
|
||||
}
|
||||
@@ -4698,7 +4338,6 @@ type DeleteTaskPayload {
|
||||
deletedTaskId: ID!
|
||||
}
|
||||
|
||||
|
||||
type CreateControlMeasureMappingPayload {
|
||||
controlEdge: ControlEdge!
|
||||
measureEdge: MeasureEdge!
|
||||
@@ -4906,30 +4545,6 @@ type DeleteStateOfApplicabilityPayload {
|
||||
deletedStateOfApplicabilityId: ID!
|
||||
}
|
||||
|
||||
type ConfirmEmailPayload {
|
||||
success: Boolean!
|
||||
}
|
||||
|
||||
type InviteUserPayload {
|
||||
invitationEdge: InvitationEdge!
|
||||
}
|
||||
|
||||
type AcceptInvitationPayload {
|
||||
invitation: Invitation!
|
||||
}
|
||||
|
||||
type DeleteInvitationPayload {
|
||||
deletedInvitationId: ID!
|
||||
}
|
||||
|
||||
type RemoveMemberPayload {
|
||||
deletedMemberId: ID!
|
||||
}
|
||||
|
||||
type UpdateMembershipPayload {
|
||||
membership: Membership!
|
||||
}
|
||||
|
||||
input VendorRiskAssessmentOrder {
|
||||
field: VendorRiskAssessmentOrderField!
|
||||
direction: OrderDirection!
|
||||
@@ -5213,7 +4828,7 @@ type Asset implements Node {
|
||||
before: CursorKey
|
||||
orderBy: VendorOrder
|
||||
): VendorConnection! @goField(forceResolver: true)
|
||||
assetType: AssetType! @goField(forceResolver: true)
|
||||
assetType: AssetType!
|
||||
dataTypesStored: String!
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
createdAt: Datetime!
|
||||
@@ -5513,162 +5128,3 @@ type CreateCustomDomainPayload {
|
||||
type DeleteCustomDomainPayload {
|
||||
deletedCustomDomainId: ID!
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# SAML Configuration Types
|
||||
# ============================================
|
||||
|
||||
type SAMLConfiguration implements Node {
|
||||
id: ID!
|
||||
organization: Organization! @goField(forceResolver: true)
|
||||
emailDomain: String!
|
||||
enabled: Boolean!
|
||||
enforcementPolicy: SAMLEnforcementPolicy!
|
||||
|
||||
# Domain verification (required before SAML can be configured)
|
||||
domainVerified: Boolean!
|
||||
domainVerificationToken: String
|
||||
domainVerifiedAt: Datetime
|
||||
|
||||
# Service Provider metadata (read-only, auto-generated)
|
||||
spEntityId: String!
|
||||
spAcsUrl: String!
|
||||
spMetadataUrl: String! @goField(forceResolver: true)
|
||||
|
||||
# Identity Provider configuration
|
||||
idpEntityId: String!
|
||||
idpSsoUrl: String!
|
||||
idpCertificate: String!
|
||||
idpMetadataUrl: String
|
||||
|
||||
# Attribute mapping
|
||||
attributeEmail: String!
|
||||
attributeFirstname: String!
|
||||
attributeLastname: String!
|
||||
attributeRole: String!
|
||||
|
||||
# Auto-signup
|
||||
autoSignupEnabled: Boolean!
|
||||
|
||||
# Test login URL for this configuration
|
||||
testLoginUrl: String! @goField(forceResolver: true)
|
||||
|
||||
createdAt: Datetime!
|
||||
updatedAt: Datetime!
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# SAML Configuration Inputs
|
||||
# ============================================
|
||||
|
||||
input CreateSAMLConfigurationInput {
|
||||
organizationId: ID!
|
||||
|
||||
# Email domain this config applies to
|
||||
emailDomain: String!
|
||||
|
||||
# Enforcement policy for this SAML configuration
|
||||
enforcementPolicy: SAMLEnforcementPolicy!
|
||||
|
||||
# SP configuration (optional - auto-generated if not provided)
|
||||
spCertificate: String
|
||||
spPrivateKey: String
|
||||
|
||||
# IdP configuration - Option 1: Provide metadata XML (recommended for Google Workspace)
|
||||
# This will automatically extract entityId, ssoUrl, and certificate from the metadata
|
||||
idpMetadataXml: String
|
||||
|
||||
# IdP configuration - Option 2: Provide individual fields manually
|
||||
# Required if idpMetadataXml is not provided
|
||||
idpEntityId: String
|
||||
idpSsoUrl: String
|
||||
idpCertificate: String
|
||||
idpMetadataUrl: String
|
||||
|
||||
# Attribute mapping (optional, defaults provided)
|
||||
attributeEmail: String
|
||||
attributeFirstname: String
|
||||
attributeLastname: String
|
||||
attributeRole: String
|
||||
|
||||
autoSignupEnabled: Boolean
|
||||
}
|
||||
|
||||
input UpdateSAMLConfigurationInput {
|
||||
id: ID!
|
||||
|
||||
enabled: Boolean
|
||||
enforcementPolicy: SAMLEnforcementPolicy
|
||||
spCertificate: String
|
||||
spPrivateKey: String
|
||||
idpEntityId: String
|
||||
idpSsoUrl: String
|
||||
idpCertificate: String
|
||||
idpMetadataUrl: String
|
||||
attributeEmail: String
|
||||
attributeFirstname: String
|
||||
attributeLastname: String
|
||||
attributeRole: String
|
||||
autoSignupEnabled: Boolean
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# Domain Verification Inputs
|
||||
# ============================================
|
||||
|
||||
input InitiateDomainVerificationInput {
|
||||
organizationId: ID!
|
||||
emailDomain: String!
|
||||
}
|
||||
|
||||
input VerifyDomainInput {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
input DeleteSAMLConfigurationInput {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
input EnableSAMLInput {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
input DisableSAMLInput {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
# ============================================
|
||||
# SAML Configuration Payloads
|
||||
# ============================================
|
||||
|
||||
type InitiateDomainVerificationPayload {
|
||||
samlConfiguration: SAMLConfiguration!
|
||||
# The TXT record value that needs to be added to DNS
|
||||
# Format: probo-verification={token}
|
||||
dnsRecord: String!
|
||||
}
|
||||
|
||||
type VerifyDomainPayload {
|
||||
samlConfiguration: SAMLConfiguration!
|
||||
verified: Boolean!
|
||||
}
|
||||
|
||||
type CreateSAMLConfigurationPayload {
|
||||
samlConfiguration: SAMLConfiguration!
|
||||
}
|
||||
|
||||
type UpdateSAMLConfigurationPayload {
|
||||
samlConfiguration: SAMLConfiguration!
|
||||
}
|
||||
|
||||
type DeleteSAMLConfigurationPayload {
|
||||
deletedSAMLConfigurationId: ID!
|
||||
}
|
||||
|
||||
type EnableSAMLPayload {
|
||||
samlConfiguration: SAMLConfiguration!
|
||||
}
|
||||
|
||||
type DisableSAMLPayload {
|
||||
samlConfiguration: SAMLConfiguration!
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,74 +0,0 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
InvitationConnection struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
Edges []*InvitationEdge `json:"edges"`
|
||||
PageInfo *PageInfo `json:"pageInfo"`
|
||||
|
||||
Resolver any
|
||||
ParentID gid.GID
|
||||
Filter *InvitationFilter
|
||||
}
|
||||
)
|
||||
|
||||
func NewInvitationConnection(
|
||||
p *page.Page[*coredata.Invitation, coredata.InvitationOrderField],
|
||||
resolver any,
|
||||
parentID gid.GID,
|
||||
filter *InvitationFilter,
|
||||
) *InvitationConnection {
|
||||
var edges = make([]*InvitationEdge, len(p.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewInvitationEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &InvitationConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
Resolver: resolver,
|
||||
ParentID: parentID,
|
||||
Filter: filter,
|
||||
}
|
||||
}
|
||||
|
||||
func NewInvitationEdge(invitation *coredata.Invitation, orderBy coredata.InvitationOrderField) *InvitationEdge {
|
||||
return &InvitationEdge{
|
||||
Cursor: invitation.CursorKey(orderBy),
|
||||
Node: NewInvitation(invitation),
|
||||
}
|
||||
}
|
||||
|
||||
func NewInvitation(i *coredata.Invitation) *Invitation {
|
||||
return &Invitation{
|
||||
ID: i.ID,
|
||||
Email: i.Email,
|
||||
FullName: i.FullName,
|
||||
Role: i.Role,
|
||||
Status: i.Status,
|
||||
ExpiresAt: i.ExpiresAt,
|
||||
AcceptedAt: i.AcceptedAt,
|
||||
CreatedAt: i.CreatedAt,
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
MembershipConnection struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
Edges []*MembershipEdge `json:"edges"`
|
||||
PageInfo *PageInfo `json:"pageInfo"`
|
||||
|
||||
Resolver any
|
||||
ParentID gid.GID
|
||||
}
|
||||
|
||||
MembershipOrderBy OrderBy[coredata.MembershipOrderField]
|
||||
)
|
||||
|
||||
func NewMembershipConnection(
|
||||
p *page.Page[*coredata.Membership, coredata.MembershipOrderField],
|
||||
resolver any,
|
||||
parentID gid.GID,
|
||||
) *MembershipConnection {
|
||||
var edges = make([]*MembershipEdge, len(p.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewMembershipEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &MembershipConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
Resolver: resolver,
|
||||
ParentID: parentID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMembershipEdge(membership *coredata.Membership, orderBy coredata.MembershipOrderField) *MembershipEdge {
|
||||
return &MembershipEdge{
|
||||
Cursor: membership.CursorKey(orderBy),
|
||||
Node: NewMembership(membership),
|
||||
}
|
||||
}
|
||||
|
||||
func NewMembership(m *coredata.Membership) *Membership {
|
||||
return &Membership{
|
||||
ID: m.ID,
|
||||
UserID: m.UserID,
|
||||
OrganizationID: m.OrganizationID,
|
||||
Role: m.Role,
|
||||
FullName: m.FullName,
|
||||
EmailAddress: m.EmailAddress,
|
||||
CreatedAt: m.CreatedAt,
|
||||
UpdatedAt: m.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -16,33 +16,8 @@ package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
OrganizationOrderBy OrderBy[coredata.OrganizationOrderField]
|
||||
)
|
||||
|
||||
func NewOrganizationConnection(page *page.Page[*coredata.Organization, coredata.OrganizationOrderField]) *OrganizationConnection {
|
||||
var edges = make([]*OrganizationEdge, len(page.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewOrganizationEdge(page.Data[i], page.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &OrganizationConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(page),
|
||||
}
|
||||
}
|
||||
|
||||
func NewOrganizationEdge(o *coredata.Organization, orderBy coredata.OrganizationOrderField) *OrganizationEdge {
|
||||
return &OrganizationEdge{
|
||||
Cursor: o.CursorKey(orderBy),
|
||||
Node: NewOrganization(o),
|
||||
}
|
||||
}
|
||||
|
||||
func NewOrganization(o *coredata.Organization) *Organization {
|
||||
return &Organization{
|
||||
ID: o.ID,
|
||||
|
||||
@@ -64,7 +64,6 @@ func NewPeopleEdge(p *coredata.People, orderBy coredata.PeopleOrderField) *Peopl
|
||||
}
|
||||
|
||||
func NewPeople(p *coredata.People) *People {
|
||||
|
||||
return &People{
|
||||
ID: p.ID,
|
||||
FullName: p.FullName,
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
)
|
||||
|
||||
func NewSAMLConfigurationWithURLs(c *coredata.SAMLConfiguration, spEntityID, spAcsURL string) *SAMLConfiguration {
|
||||
return &SAMLConfiguration{
|
||||
ID: c.ID,
|
||||
EmailDomain: c.EmailDomain,
|
||||
Enabled: c.Enabled,
|
||||
EnforcementPolicy: c.EnforcementPolicy,
|
||||
DomainVerified: c.DomainVerified,
|
||||
DomainVerificationToken: c.DomainVerificationToken,
|
||||
DomainVerifiedAt: c.DomainVerifiedAt,
|
||||
SpEntityID: spEntityID,
|
||||
SpAcsURL: spAcsURL,
|
||||
IdpEntityID: c.IdPEntityID,
|
||||
IdpSsoURL: c.IdPSsoURL,
|
||||
IdpCertificate: c.IdPCertificate,
|
||||
IdpMetadataURL: c.IdPMetadataURL,
|
||||
AttributeEmail: c.AttributeEmail,
|
||||
AttributeFirstname: c.AttributeFirstname,
|
||||
AttributeLastname: c.AttributeLastname,
|
||||
AttributeRole: c.AttributeRole,
|
||||
AutoSignupEnabled: c.AutoSignupEnabled,
|
||||
CreatedAt: c.CreatedAt,
|
||||
UpdatedAt: c.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@@ -65,7 +65,13 @@ func NewStateOfApplicabilityEdge(soa *coredata.StateOfApplicability, orderBy cor
|
||||
|
||||
func NewStateOfApplicability(soa *coredata.StateOfApplicability) *StateOfApplicability {
|
||||
return &StateOfApplicability{
|
||||
ID: soa.ID,
|
||||
ID: soa.ID,
|
||||
Organization: &Organization{
|
||||
ID: soa.OrganizationID,
|
||||
},
|
||||
Owner: &People{
|
||||
ID: soa.OwnerID,
|
||||
},
|
||||
Name: soa.Name,
|
||||
SourceID: soa.SourceID,
|
||||
SnapshotID: soa.SnapshotID,
|
||||
|
||||
@@ -61,7 +61,7 @@ func NewTaskEdge(t *coredata.Task, orderBy coredata.TaskOrderField) *TaskEdge {
|
||||
}
|
||||
|
||||
func NewTask(t *coredata.Task) *Task {
|
||||
return &Task{
|
||||
node := &Task{
|
||||
ID: t.ID,
|
||||
Name: t.Name,
|
||||
Description: t.Description,
|
||||
@@ -71,4 +71,12 @@ func NewTask(t *coredata.Task) *Task {
|
||||
UpdatedAt: t.UpdatedAt,
|
||||
Deadline: t.Deadline,
|
||||
}
|
||||
|
||||
if t.MeasureID != nil {
|
||||
node.Measure = &Measure{
|
||||
ID: *t.MeasureID,
|
||||
}
|
||||
}
|
||||
|
||||
return node
|
||||
}
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/99designs/gqlgen/graphql"
|
||||
@@ -21,14 +17,6 @@ type Node interface {
|
||||
GetID() gid.GID
|
||||
}
|
||||
|
||||
type AcceptInvitationInput struct {
|
||||
InvitationID gid.GID `json:"invitationId"`
|
||||
}
|
||||
|
||||
type AcceptInvitationPayload struct {
|
||||
Invitation *Invitation `json:"invitation"`
|
||||
}
|
||||
|
||||
type AssessVendorInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
WebsiteURL string `json:"websiteUrl"`
|
||||
@@ -150,14 +138,6 @@ type CancelSignatureRequestPayload struct {
|
||||
DeletedDocumentVersionSignatureID gid.GID `json:"deletedDocumentVersionSignatureId"`
|
||||
}
|
||||
|
||||
type ConfirmEmailInput struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
type ConfirmEmailPayload struct {
|
||||
Success bool `json:"success"`
|
||||
}
|
||||
|
||||
type ContinualImprovement struct {
|
||||
ID gid.GID `json:"id"`
|
||||
SnapshotID *gid.GID `json:"snapshotId,omitempty"`
|
||||
@@ -441,14 +421,6 @@ type CreateObligationPayload struct {
|
||||
ObligationEdge *ObligationEdge `json:"obligationEdge"`
|
||||
}
|
||||
|
||||
type CreateOrganizationInput struct {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
type CreateOrganizationPayload struct {
|
||||
OrganizationEdge *OrganizationEdge `json:"organizationEdge"`
|
||||
}
|
||||
|
||||
type CreatePeopleInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
FullName string `json:"fullName"`
|
||||
@@ -555,28 +527,6 @@ type CreateRiskPayload struct {
|
||||
RiskEdge *RiskEdge `json:"riskEdge"`
|
||||
}
|
||||
|
||||
type CreateSAMLConfigurationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
EmailDomain string `json:"emailDomain"`
|
||||
EnforcementPolicy coredata.SAMLEnforcementPolicy `json:"enforcementPolicy"`
|
||||
SpCertificate *string `json:"spCertificate,omitempty"`
|
||||
SpPrivateKey *string `json:"spPrivateKey,omitempty"`
|
||||
IdpMetadataXML *string `json:"idpMetadataXml,omitempty"`
|
||||
IdpEntityID *string `json:"idpEntityId,omitempty"`
|
||||
IdpSsoURL *string `json:"idpSsoUrl,omitempty"`
|
||||
IdpCertificate *string `json:"idpCertificate,omitempty"`
|
||||
IdpMetadataURL *string `json:"idpMetadataUrl,omitempty"`
|
||||
AttributeEmail *string `json:"attributeEmail,omitempty"`
|
||||
AttributeFirstname *string `json:"attributeFirstname,omitempty"`
|
||||
AttributeLastname *string `json:"attributeLastname,omitempty"`
|
||||
AttributeRole *string `json:"attributeRole,omitempty"`
|
||||
AutoSignupEnabled *bool `json:"autoSignupEnabled,omitempty"`
|
||||
}
|
||||
|
||||
type CreateSAMLConfigurationPayload struct {
|
||||
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration"`
|
||||
}
|
||||
|
||||
type CreateSnapshotInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Name string `json:"name"`
|
||||
@@ -936,14 +886,6 @@ type DeleteFrameworkPayload struct {
|
||||
DeletedFrameworkID gid.GID `json:"deletedFrameworkId"`
|
||||
}
|
||||
|
||||
type DeleteInvitationInput struct {
|
||||
InvitationID gid.GID `json:"invitationId"`
|
||||
}
|
||||
|
||||
type DeleteInvitationPayload struct {
|
||||
DeletedInvitationID gid.GID `json:"deletedInvitationId"`
|
||||
}
|
||||
|
||||
type DeleteMeasureInput struct {
|
||||
MeasureID gid.GID `json:"measureId"`
|
||||
}
|
||||
@@ -976,22 +918,6 @@ type DeleteObligationPayload struct {
|
||||
DeletedObligationID gid.GID `json:"deletedObligationId"`
|
||||
}
|
||||
|
||||
type DeleteOrganizationHorizontalLogoInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
}
|
||||
|
||||
type DeleteOrganizationHorizontalLogoPayload struct {
|
||||
Organization *Organization `json:"organization"`
|
||||
}
|
||||
|
||||
type DeleteOrganizationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
}
|
||||
|
||||
type DeleteOrganizationPayload struct {
|
||||
DeletedOrganizationID gid.GID `json:"deletedOrganizationId"`
|
||||
}
|
||||
|
||||
type DeletePeopleInput struct {
|
||||
PeopleID gid.GID `json:"peopleId"`
|
||||
}
|
||||
@@ -1054,14 +980,6 @@ type DeleteRiskPayload struct {
|
||||
DeletedRiskID gid.GID `json:"deletedRiskId"`
|
||||
}
|
||||
|
||||
type DeleteSAMLConfigurationInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
}
|
||||
|
||||
type DeleteSAMLConfigurationPayload struct {
|
||||
DeletedSAMLConfigurationID gid.GID `json:"deletedSAMLConfigurationId"`
|
||||
}
|
||||
|
||||
type DeleteSnapshotInput struct {
|
||||
SnapshotID gid.GID `json:"snapshotId"`
|
||||
}
|
||||
@@ -1185,14 +1103,6 @@ type DeleteVendorServicePayload struct {
|
||||
DeletedVendorServiceID gid.GID `json:"deletedVendorServiceId"`
|
||||
}
|
||||
|
||||
type DisableSAMLInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
}
|
||||
|
||||
type DisableSAMLPayload struct {
|
||||
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration"`
|
||||
}
|
||||
|
||||
type Document struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Title string `json:"title"`
|
||||
@@ -1268,14 +1178,6 @@ type DocumentVersionSignatureOrder struct {
|
||||
Direction page.OrderDirection `json:"direction"`
|
||||
}
|
||||
|
||||
type EnableSAMLInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
}
|
||||
|
||||
type EnableSAMLPayload struct {
|
||||
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration"`
|
||||
}
|
||||
|
||||
type Evidence struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Size int `json:"size"`
|
||||
@@ -1443,57 +1345,6 @@ type ImportMeasurePayload struct {
|
||||
MeasureEdges []*MeasureEdge `json:"measureEdges"`
|
||||
}
|
||||
|
||||
type InitiateDomainVerificationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
EmailDomain string `json:"emailDomain"`
|
||||
}
|
||||
|
||||
type InitiateDomainVerificationPayload struct {
|
||||
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration"`
|
||||
DNSRecord string `json:"dnsRecord"`
|
||||
}
|
||||
|
||||
type Invitation struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Email mail.Addr `json:"email"`
|
||||
FullName string `json:"fullName"`
|
||||
Role coredata.MembershipRole `json:"role"`
|
||||
Status coredata.InvitationStatus `json:"status"`
|
||||
ExpiresAt time.Time `json:"expiresAt"`
|
||||
AcceptedAt *time.Time `json:"acceptedAt,omitempty"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
Organization *Organization `json:"organization"`
|
||||
}
|
||||
|
||||
func (Invitation) IsNode() {}
|
||||
func (this Invitation) GetID() gid.GID { return this.ID }
|
||||
|
||||
type InvitationEdge struct {
|
||||
Cursor page.CursorKey `json:"cursor"`
|
||||
Node *Invitation `json:"node"`
|
||||
}
|
||||
|
||||
type InvitationFilter struct {
|
||||
Statuses []coredata.InvitationStatus `json:"statuses,omitempty"`
|
||||
}
|
||||
|
||||
type InvitationOrder struct {
|
||||
Direction page.OrderDirection `json:"direction"`
|
||||
Field coredata.InvitationOrderField `json:"field"`
|
||||
}
|
||||
|
||||
type InviteUserInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Email mail.Addr `json:"email"`
|
||||
FullName string `json:"fullName"`
|
||||
Role coredata.MembershipRole `json:"role"`
|
||||
CreatePeople bool `json:"createPeople"`
|
||||
}
|
||||
|
||||
type InviteUserPayload struct {
|
||||
InvitationEdge *InvitationEdge `json:"invitationEdge"`
|
||||
}
|
||||
|
||||
type Measure struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Category string `json:"category"`
|
||||
@@ -1540,26 +1391,6 @@ type MeetingEdge struct {
|
||||
Node *Meeting `json:"node"`
|
||||
}
|
||||
|
||||
type Membership struct {
|
||||
ID gid.GID `json:"id"`
|
||||
UserID gid.GID `json:"userID"`
|
||||
OrganizationID gid.GID `json:"organizationID"`
|
||||
Role coredata.MembershipRole `json:"role"`
|
||||
FullName string `json:"fullName"`
|
||||
EmailAddress mail.Addr `json:"emailAddress"`
|
||||
AuthMethod coredata.UserAuthMethod `json:"authMethod"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (Membership) IsNode() {}
|
||||
func (this Membership) GetID() gid.GID { return this.ID }
|
||||
|
||||
type MembershipEdge struct {
|
||||
Cursor page.CursorKey `json:"cursor"`
|
||||
Node *Membership `json:"node"`
|
||||
}
|
||||
|
||||
type Mutation struct {
|
||||
}
|
||||
|
||||
@@ -1634,8 +1465,6 @@ type Organization struct {
|
||||
Email *string `json:"email,omitempty"`
|
||||
HeadquarterAddress *string `json:"headquarterAddress,omitempty"`
|
||||
Context *OrganizationContext `json:"context,omitempty"`
|
||||
Memberships *MembershipConnection `json:"memberships"`
|
||||
Invitations *InvitationConnection `json:"invitations"`
|
||||
SlackConnections *SlackConnectionConnection `json:"slackConnections"`
|
||||
Frameworks *FrameworkConnection `json:"frameworks"`
|
||||
Controls *ControlConnection `json:"controls"`
|
||||
@@ -1661,7 +1490,6 @@ type Organization struct {
|
||||
TrustCenterFiles *TrustCenterFileConnection `json:"trustCenterFiles"`
|
||||
TrustCenter *TrustCenter `json:"trustCenter,omitempty"`
|
||||
CustomDomain *CustomDomain `json:"customDomain,omitempty"`
|
||||
SamlConfigurations []*SAMLConfiguration `json:"samlConfigurations"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
@@ -1669,21 +1497,11 @@ type Organization struct {
|
||||
func (Organization) IsNode() {}
|
||||
func (this Organization) GetID() gid.GID { return this.ID }
|
||||
|
||||
type OrganizationConnection struct {
|
||||
Edges []*OrganizationEdge `json:"edges"`
|
||||
PageInfo *PageInfo `json:"pageInfo"`
|
||||
}
|
||||
|
||||
type OrganizationContext struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Summary *string `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
type OrganizationEdge struct {
|
||||
Cursor page.CursorKey `json:"cursor"`
|
||||
Node *Organization `json:"node"`
|
||||
}
|
||||
|
||||
type OrganizationOrder struct {
|
||||
Direction page.OrderDirection `json:"direction"`
|
||||
Field coredata.OrganizationOrderField `json:"field"`
|
||||
@@ -1777,15 +1595,6 @@ type PublishDocumentVersionPayload struct {
|
||||
type Query struct {
|
||||
}
|
||||
|
||||
type RemoveMemberInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
MemberID gid.GID `json:"memberId"`
|
||||
}
|
||||
|
||||
type RemoveMemberPayload struct {
|
||||
DeletedMemberID gid.GID `json:"deletedMemberId"`
|
||||
}
|
||||
|
||||
type Report struct {
|
||||
ID gid.GID `json:"id"`
|
||||
ObjectKey string `json:"objectKey"`
|
||||
@@ -1880,35 +1689,6 @@ type RiskFilter struct {
|
||||
SnapshotID *gid.GID `json:"snapshotId,omitempty"`
|
||||
}
|
||||
|
||||
type SAMLConfiguration struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Organization *Organization `json:"organization"`
|
||||
EmailDomain string `json:"emailDomain"`
|
||||
Enabled bool `json:"enabled"`
|
||||
EnforcementPolicy coredata.SAMLEnforcementPolicy `json:"enforcementPolicy"`
|
||||
DomainVerified bool `json:"domainVerified"`
|
||||
DomainVerificationToken *string `json:"domainVerificationToken,omitempty"`
|
||||
DomainVerifiedAt *time.Time `json:"domainVerifiedAt,omitempty"`
|
||||
SpEntityID string `json:"spEntityId"`
|
||||
SpAcsURL string `json:"spAcsUrl"`
|
||||
SpMetadataURL string `json:"spMetadataUrl"`
|
||||
IdpEntityID string `json:"idpEntityId"`
|
||||
IdpSsoURL string `json:"idpSsoUrl"`
|
||||
IdpCertificate string `json:"idpCertificate"`
|
||||
IdpMetadataURL *string `json:"idpMetadataUrl,omitempty"`
|
||||
AttributeEmail string `json:"attributeEmail"`
|
||||
AttributeFirstname string `json:"attributeFirstname"`
|
||||
AttributeLastname string `json:"attributeLastname"`
|
||||
AttributeRole string `json:"attributeRole"`
|
||||
AutoSignupEnabled bool `json:"autoSignupEnabled"`
|
||||
TestLoginURL string `json:"testLoginUrl"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (SAMLConfiguration) IsNode() {}
|
||||
func (this SAMLConfiguration) GetID() gid.GID { return this.ID }
|
||||
|
||||
type SendSigningNotificationsInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
}
|
||||
@@ -2289,16 +2069,6 @@ type UpdateMeetingPayload struct {
|
||||
Meeting *Meeting `json:"meeting"`
|
||||
}
|
||||
|
||||
type UpdateMembershipInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
MemberID gid.GID `json:"memberId"`
|
||||
Role coredata.MembershipRole `json:"role"`
|
||||
}
|
||||
|
||||
type UpdateMembershipPayload struct {
|
||||
Membership *Membership `json:"membership"`
|
||||
}
|
||||
|
||||
type UpdateNonconformityInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
ReferenceID *string `json:"referenceId,omitempty"`
|
||||
@@ -2344,30 +2114,15 @@ type UpdateOrganizationContextPayload struct {
|
||||
Context *OrganizationContext `json:"context"`
|
||||
}
|
||||
|
||||
type UpdateOrganizationInput struct {
|
||||
OrganizationID gid.GID `json:"organizationId"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Description graphql.Omittable[*string] `json:"description,omitempty"`
|
||||
WebsiteURL graphql.Omittable[*string] `json:"websiteUrl,omitempty"`
|
||||
Email graphql.Omittable[*string] `json:"email,omitempty"`
|
||||
HeadquarterAddress graphql.Omittable[*string] `json:"headquarterAddress,omitempty"`
|
||||
LogoFile *graphql.Upload `json:"logoFile,omitempty"`
|
||||
HorizontalLogoFile *graphql.Upload `json:"horizontalLogoFile,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateOrganizationPayload struct {
|
||||
Organization *Organization `json:"organization"`
|
||||
}
|
||||
|
||||
type UpdatePeopleInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
FullName *string `json:"fullName,omitempty"`
|
||||
PrimaryEmailAddress *mail.Addr `json:"primaryEmailAddress,omitempty"`
|
||||
AdditionalEmailAddresses []mail.Addr `json:"additionalEmailAddresses,omitempty"`
|
||||
Kind *coredata.PeopleKind `json:"kind,omitempty"`
|
||||
Position graphql.Omittable[*string] `json:"position,omitempty"`
|
||||
ContractStartDate graphql.Omittable[*time.Time] `json:"contractStartDate,omitempty"`
|
||||
ContractEndDate graphql.Omittable[*time.Time] `json:"contractEndDate,omitempty"`
|
||||
ID gid.GID `json:"id"`
|
||||
FullName *string `json:"fullName,omitempty"`
|
||||
PrimaryEmailAddress *mail.Addr `json:"primaryEmailAddress,omitempty"`
|
||||
AdditionalEmailAddresses graphql.Omittable[[]mail.Addr] `json:"additionalEmailAddresses,omitempty"`
|
||||
Kind *coredata.PeopleKind `json:"kind,omitempty"`
|
||||
Position graphql.Omittable[*string] `json:"position,omitempty"`
|
||||
ContractStartDate graphql.Omittable[*time.Time] `json:"contractStartDate,omitempty"`
|
||||
ContractEndDate graphql.Omittable[*time.Time] `json:"contractEndDate,omitempty"`
|
||||
}
|
||||
|
||||
type UpdatePeoplePayload struct {
|
||||
@@ -2435,27 +2190,6 @@ type UpdateRiskPayload struct {
|
||||
Risk *Risk `json:"risk"`
|
||||
}
|
||||
|
||||
type UpdateSAMLConfigurationInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Enabled *bool `json:"enabled,omitempty"`
|
||||
EnforcementPolicy *coredata.SAMLEnforcementPolicy `json:"enforcementPolicy,omitempty"`
|
||||
SpCertificate *string `json:"spCertificate,omitempty"`
|
||||
SpPrivateKey *string `json:"spPrivateKey,omitempty"`
|
||||
IdpEntityID *string `json:"idpEntityId,omitempty"`
|
||||
IdpSsoURL *string `json:"idpSsoUrl,omitempty"`
|
||||
IdpCertificate *string `json:"idpCertificate,omitempty"`
|
||||
IdpMetadataURL *string `json:"idpMetadataUrl,omitempty"`
|
||||
AttributeEmail *string `json:"attributeEmail,omitempty"`
|
||||
AttributeFirstname *string `json:"attributeFirstname,omitempty"`
|
||||
AttributeLastname *string `json:"attributeLastname,omitempty"`
|
||||
AttributeRole *string `json:"attributeRole,omitempty"`
|
||||
AutoSignupEnabled *bool `json:"autoSignupEnabled,omitempty"`
|
||||
}
|
||||
|
||||
type UpdateSAMLConfigurationPayload struct {
|
||||
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration"`
|
||||
}
|
||||
|
||||
type UpdateStateOfApplicabilityInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
@@ -2676,22 +2410,6 @@ type UploadVendorDataPrivacyAgreementPayload struct {
|
||||
VendorDataPrivacyAgreement *VendorDataPrivacyAgreement `json:"vendorDataPrivacyAgreement"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
ID gid.GID `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
Email mail.Addr `json:"email"`
|
||||
CreatedAt time.Time `json:"createdAt"`
|
||||
UpdatedAt time.Time `json:"updatedAt"`
|
||||
}
|
||||
|
||||
func (User) IsNode() {}
|
||||
func (this User) GetID() gid.GID { return this.ID }
|
||||
|
||||
type UserEdge struct {
|
||||
Cursor page.CursorKey `json:"cursor"`
|
||||
Node *User `json:"node"`
|
||||
}
|
||||
|
||||
type Vendor struct {
|
||||
ID gid.GID `json:"id"`
|
||||
SnapshotID *gid.GID `json:"snapshotId,omitempty"`
|
||||
@@ -2867,80 +2585,8 @@ type VendorServiceEdge struct {
|
||||
Node *VendorService `json:"node"`
|
||||
}
|
||||
|
||||
type VerifyDomainInput struct {
|
||||
ID gid.GID `json:"id"`
|
||||
}
|
||||
|
||||
type VerifyDomainPayload struct {
|
||||
SamlConfiguration *SAMLConfiguration `json:"samlConfiguration"`
|
||||
Verified bool `json:"verified"`
|
||||
}
|
||||
|
||||
type Viewer struct {
|
||||
ID gid.GID `json:"id"`
|
||||
User *User `json:"user"`
|
||||
Organizations *OrganizationConnection `json:"organizations"`
|
||||
SignableDocuments *SignableDocumentConnection `json:"signableDocuments"`
|
||||
SignableDocument *SignableDocument `json:"signableDocument,omitempty"`
|
||||
}
|
||||
|
||||
type Role string
|
||||
|
||||
const (
|
||||
RoleOwner Role = "OWNER"
|
||||
RoleAdmin Role = "ADMIN"
|
||||
RoleViewer Role = "VIEWER"
|
||||
RoleAuditor Role = "AUDITOR"
|
||||
RoleFull Role = "FULL"
|
||||
)
|
||||
|
||||
var AllRole = []Role{
|
||||
RoleOwner,
|
||||
RoleAdmin,
|
||||
RoleViewer,
|
||||
RoleAuditor,
|
||||
RoleFull,
|
||||
}
|
||||
|
||||
func (e Role) IsValid() bool {
|
||||
switch e {
|
||||
case RoleOwner, RoleAdmin, RoleViewer, RoleAuditor, RoleFull:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (e Role) String() string {
|
||||
return string(e)
|
||||
}
|
||||
|
||||
func (e *Role) UnmarshalGQL(v any) error {
|
||||
str, ok := v.(string)
|
||||
if !ok {
|
||||
return fmt.Errorf("enums must be strings")
|
||||
}
|
||||
|
||||
*e = Role(str)
|
||||
if !e.IsValid() {
|
||||
return fmt.Errorf("%s is not a valid Role", str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e Role) MarshalGQL(w io.Writer) {
|
||||
fmt.Fprint(w, strconv.Quote(e.String()))
|
||||
}
|
||||
|
||||
func (e *Role) UnmarshalJSON(b []byte) error {
|
||||
s, err := strconv.Unquote(string(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return e.UnmarshalGQL(s)
|
||||
}
|
||||
|
||||
func (e Role) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
e.MarshalGQL(&buf)
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
|
||||
//
|
||||
// Permission to use, copy, modify, and/or distribute this software for any
|
||||
// purpose with or without fee is hereby granted, provided that the above
|
||||
// copyright notice and this permission notice appear in all copies.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
// PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"go.probo.inc/probo/pkg/coredata"
|
||||
"go.probo.inc/probo/pkg/gid"
|
||||
"go.probo.inc/probo/pkg/page"
|
||||
)
|
||||
|
||||
type (
|
||||
UserConnection struct {
|
||||
TotalCount int `json:"totalCount"`
|
||||
Edges []*UserEdge `json:"edges"`
|
||||
PageInfo *PageInfo `json:"pageInfo"`
|
||||
|
||||
Resolver any
|
||||
ParentID gid.GID
|
||||
}
|
||||
|
||||
UserOrderBy OrderBy[coredata.UserOrderField]
|
||||
)
|
||||
|
||||
func NewUserConnection(
|
||||
p *page.Page[*coredata.User, coredata.UserOrderField],
|
||||
resolver any,
|
||||
parentID gid.GID,
|
||||
) *UserConnection {
|
||||
var edges = make([]*UserEdge, len(p.Data))
|
||||
|
||||
for i := range edges {
|
||||
edges[i] = NewUserEdge(p.Data[i], p.Cursor.OrderBy.Field)
|
||||
}
|
||||
|
||||
return &UserConnection{
|
||||
Edges: edges,
|
||||
PageInfo: NewPageInfo(p),
|
||||
Resolver: resolver,
|
||||
ParentID: parentID,
|
||||
}
|
||||
}
|
||||
|
||||
func NewUserEdge(user *coredata.User, orderBy coredata.UserOrderField) *UserEdge {
|
||||
return &UserEdge{
|
||||
Cursor: user.CursorKey(orderBy),
|
||||
Node: NewUser(user),
|
||||
}
|
||||
}
|
||||
|
||||
func NewUser(u *coredata.User) *User {
|
||||
return &User{
|
||||
ID: u.ID,
|
||||
Email: u.EmailAddress,
|
||||
FullName: u.FullName,
|
||||
CreatedAt: u.CreatedAt,
|
||||
UpdatedAt: u.UpdatedAt,
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user