diff --git a/cmd/internal/openapi/client.gen.go b/cmd/internal/openapi/client.gen.go index a23eecdf..e07b7f1b 100644 --- a/cmd/internal/openapi/client.gen.go +++ b/cmd/internal/openapi/client.gen.go @@ -7631,6 +7631,27 @@ type PostApiAuthOauth2Register201JSONResponseBodyResponseTypes string // PostApiAuthOauth2Register201JSONResponseBodyType defines parameters for PostApiAuthOauth2Register. type PostApiAuthOauth2Register201JSONResponseBodyType string +// GetDynamicOAuthClientRegistration200JSONResponseBody defines parameters for GetDynamicOAuthClientRegistration. +type GetDynamicOAuthClientRegistration200JSONResponseBody struct { + ClientId string `json:"client_id"` + RegistrationAccessToken string `json:"registration_access_token"` + RegistrationClientUri string `json:"registration_client_uri"` + Scope *string `json:"scope,omitempty"` + AdditionalProperties map[string]interface{} `json:"-"` +} + +// UpdateDynamicOAuthClientRegistrationJSONBody defines parameters for UpdateDynamicOAuthClientRegistration. +type UpdateDynamicOAuthClientRegistrationJSONBody map[string]interface{} + +// UpdateDynamicOAuthClientRegistration200JSONResponseBody defines parameters for UpdateDynamicOAuthClientRegistration. +type UpdateDynamicOAuthClientRegistration200JSONResponseBody struct { + ClientId string `json:"client_id"` + RegistrationAccessToken string `json:"registration_access_token"` + RegistrationClientUri string `json:"registration_client_uri"` + Scope *string `json:"scope,omitempty"` + AdditionalProperties map[string]interface{} `json:"-"` +} + // PostApiAuthOauth2RevokeJSONBody defines parameters for PostApiAuthOauth2Revoke. type PostApiAuthOauth2RevokeJSONBody struct { // ClientId OAuth2 client ID @@ -9114,6 +9135,9 @@ type PostApiAuthOauth2PublicClientPreloginJSONRequestBody PostApiAuthOauth2Publi // PostApiAuthOauth2RegisterJSONRequestBody defines body for PostApiAuthOauth2Register for application/json ContentType. type PostApiAuthOauth2RegisterJSONRequestBody PostApiAuthOauth2RegisterJSONBody +// UpdateDynamicOAuthClientRegistrationJSONRequestBody defines body for UpdateDynamicOAuthClientRegistration for application/json ContentType. +type UpdateDynamicOAuthClientRegistrationJSONRequestBody UpdateDynamicOAuthClientRegistrationJSONBody + // PostApiAuthOauth2RevokeJSONRequestBody defines body for PostApiAuthOauth2Revoke for application/json ContentType. type PostApiAuthOauth2RevokeJSONRequestBody PostApiAuthOauth2RevokeJSONBody @@ -9378,6 +9402,220 @@ type GrantUserEntitlementJSONRequestBody GrantUserEntitlementJSONBody // UpdateUserEntitlementJSONRequestBody defines body for UpdateUserEntitlement for application/json ContentType. type UpdateUserEntitlementJSONRequestBody UpdateUserEntitlementJSONBody +// Getter for additional properties for GetDynamicOAuthClientRegistration200JSONResponseBody. Returns the specified +// element and whether it was found +func (a GetDynamicOAuthClientRegistration200JSONResponseBody) Get(fieldName string) (value interface{}, found bool) { + if a.AdditionalProperties != nil { + value, found = a.AdditionalProperties[fieldName] + } + return +} + +// Setter for additional properties for GetDynamicOAuthClientRegistration200JSONResponseBody +func (a *GetDynamicOAuthClientRegistration200JSONResponseBody) Set(fieldName string, value interface{}) { + if a.AdditionalProperties == nil { + a.AdditionalProperties = make(map[string]interface{}) + } + a.AdditionalProperties[fieldName] = value +} + +// Override default JSON handling for GetDynamicOAuthClientRegistration200JSONResponseBody to handle AdditionalProperties +func (a *GetDynamicOAuthClientRegistration200JSONResponseBody) UnmarshalJSON(b []byte) error { + object := make(map[string]json.RawMessage) + err := json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["client_id"]; found { + err = json.Unmarshal(raw, &a.ClientId) + if err != nil { + return fmt.Errorf("error reading 'client_id': %w", err) + } + delete(object, "client_id") + } + + if raw, found := object["registration_access_token"]; found { + err = json.Unmarshal(raw, &a.RegistrationAccessToken) + if err != nil { + return fmt.Errorf("error reading 'registration_access_token': %w", err) + } + delete(object, "registration_access_token") + } + + if raw, found := object["registration_client_uri"]; found { + err = json.Unmarshal(raw, &a.RegistrationClientUri) + if err != nil { + return fmt.Errorf("error reading 'registration_client_uri': %w", err) + } + delete(object, "registration_client_uri") + } + + if raw, found := object["scope"]; found { + err = json.Unmarshal(raw, &a.Scope) + if err != nil { + return fmt.Errorf("error reading 'scope': %w", err) + } + delete(object, "scope") + } + + if len(object) != 0 { + a.AdditionalProperties = make(map[string]interface{}) + for fieldName, fieldBuf := range object { + var fieldVal interface{} + err := json.Unmarshal(fieldBuf, &fieldVal) + if err != nil { + return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err) + } + a.AdditionalProperties[fieldName] = fieldVal + } + } + return nil +} + +// Override default JSON handling for GetDynamicOAuthClientRegistration200JSONResponseBody to handle AdditionalProperties +func (a GetDynamicOAuthClientRegistration200JSONResponseBody) MarshalJSON() ([]byte, error) { + var err error + object := make(map[string]json.RawMessage) + + object["client_id"], err = json.Marshal(a.ClientId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'client_id': %w", err) + } + + object["registration_access_token"], err = json.Marshal(a.RegistrationAccessToken) + if err != nil { + return nil, fmt.Errorf("error marshaling 'registration_access_token': %w", err) + } + + object["registration_client_uri"], err = json.Marshal(a.RegistrationClientUri) + if err != nil { + return nil, fmt.Errorf("error marshaling 'registration_client_uri': %w", err) + } + + if a.Scope != nil { + object["scope"], err = json.Marshal(a.Scope) + if err != nil { + return nil, fmt.Errorf("error marshaling 'scope': %w", err) + } + } + + for fieldName, field := range a.AdditionalProperties { + object[fieldName], err = json.Marshal(field) + if err != nil { + return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err) + } + } + return json.Marshal(object) +} + +// Getter for additional properties for UpdateDynamicOAuthClientRegistration200JSONResponseBody. Returns the specified +// element and whether it was found +func (a UpdateDynamicOAuthClientRegistration200JSONResponseBody) Get(fieldName string) (value interface{}, found bool) { + if a.AdditionalProperties != nil { + value, found = a.AdditionalProperties[fieldName] + } + return +} + +// Setter for additional properties for UpdateDynamicOAuthClientRegistration200JSONResponseBody +func (a *UpdateDynamicOAuthClientRegistration200JSONResponseBody) Set(fieldName string, value interface{}) { + if a.AdditionalProperties == nil { + a.AdditionalProperties = make(map[string]interface{}) + } + a.AdditionalProperties[fieldName] = value +} + +// Override default JSON handling for UpdateDynamicOAuthClientRegistration200JSONResponseBody to handle AdditionalProperties +func (a *UpdateDynamicOAuthClientRegistration200JSONResponseBody) UnmarshalJSON(b []byte) error { + object := make(map[string]json.RawMessage) + err := json.Unmarshal(b, &object) + if err != nil { + return err + } + + if raw, found := object["client_id"]; found { + err = json.Unmarshal(raw, &a.ClientId) + if err != nil { + return fmt.Errorf("error reading 'client_id': %w", err) + } + delete(object, "client_id") + } + + if raw, found := object["registration_access_token"]; found { + err = json.Unmarshal(raw, &a.RegistrationAccessToken) + if err != nil { + return fmt.Errorf("error reading 'registration_access_token': %w", err) + } + delete(object, "registration_access_token") + } + + if raw, found := object["registration_client_uri"]; found { + err = json.Unmarshal(raw, &a.RegistrationClientUri) + if err != nil { + return fmt.Errorf("error reading 'registration_client_uri': %w", err) + } + delete(object, "registration_client_uri") + } + + if raw, found := object["scope"]; found { + err = json.Unmarshal(raw, &a.Scope) + if err != nil { + return fmt.Errorf("error reading 'scope': %w", err) + } + delete(object, "scope") + } + + if len(object) != 0 { + a.AdditionalProperties = make(map[string]interface{}) + for fieldName, fieldBuf := range object { + var fieldVal interface{} + err := json.Unmarshal(fieldBuf, &fieldVal) + if err != nil { + return fmt.Errorf("error unmarshaling field %s: %w", fieldName, err) + } + a.AdditionalProperties[fieldName] = fieldVal + } + } + return nil +} + +// Override default JSON handling for UpdateDynamicOAuthClientRegistration200JSONResponseBody to handle AdditionalProperties +func (a UpdateDynamicOAuthClientRegistration200JSONResponseBody) MarshalJSON() ([]byte, error) { + var err error + object := make(map[string]json.RawMessage) + + object["client_id"], err = json.Marshal(a.ClientId) + if err != nil { + return nil, fmt.Errorf("error marshaling 'client_id': %w", err) + } + + object["registration_access_token"], err = json.Marshal(a.RegistrationAccessToken) + if err != nil { + return nil, fmt.Errorf("error marshaling 'registration_access_token': %w", err) + } + + object["registration_client_uri"], err = json.Marshal(a.RegistrationClientUri) + if err != nil { + return nil, fmt.Errorf("error marshaling 'registration_client_uri': %w", err) + } + + if a.Scope != nil { + object["scope"], err = json.Marshal(a.Scope) + if err != nil { + return nil, fmt.Errorf("error marshaling 'scope': %w", err) + } + } + + for fieldName, field := range a.AdditionalProperties { + object[fieldName], err = json.Marshal(field) + if err != nil { + return nil, fmt.Errorf("error marshaling '%s': %w", fieldName, err) + } + } + return json.Marshal(object) +} + // AsCloudflareSaasImageDomainSettingsCloudflare0 returns the union data inside the CloudflareSaasImageDomainSettings_Cloudflare as a CloudflareSaasImageDomainSettingsCloudflare0 func (t CloudflareSaasImageDomainSettings_Cloudflare) AsCloudflareSaasImageDomainSettingsCloudflare0() (CloudflareSaasImageDomainSettingsCloudflare0, error) { var body CloudflareSaasImageDomainSettingsCloudflare0 @@ -11293,6 +11531,17 @@ type ClientInterface interface { PostApiAuthOauth2Register(ctx context.Context, body PostApiAuthOauth2RegisterJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // DeleteDynamicOAuthClientRegistration request + DeleteDynamicOAuthClientRegistration(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetDynamicOAuthClientRegistration request + GetDynamicOAuthClientRegistration(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UpdateDynamicOAuthClientRegistrationWithBody request with any body + UpdateDynamicOAuthClientRegistrationWithBody(ctx context.Context, clientId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) + + UpdateDynamicOAuthClientRegistration(ctx context.Context, clientId string, body UpdateDynamicOAuthClientRegistrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) + // PostApiAuthOauth2RevokeWithBody request with any body PostApiAuthOauth2RevokeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -13243,6 +13492,54 @@ func (c *Client) PostApiAuthOauth2Register(ctx context.Context, body PostApiAuth return c.Client.Do(req) } +func (c *Client) DeleteDynamicOAuthClientRegistration(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewDeleteDynamicOAuthClientRegistrationRequest(c.Server, clientId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetDynamicOAuthClientRegistration(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetDynamicOAuthClientRegistrationRequest(c.Server, clientId) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UpdateDynamicOAuthClientRegistrationWithBody(ctx context.Context, clientId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateDynamicOAuthClientRegistrationRequestWithBody(c.Server, clientId, contentType, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UpdateDynamicOAuthClientRegistration(ctx context.Context, clientId string, body UpdateDynamicOAuthClientRegistrationJSONRequestBody, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUpdateDynamicOAuthClientRegistrationRequest(c.Server, clientId, body) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) PostApiAuthOauth2RevokeWithBody(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewPostApiAuthOauth2RevokeRequestWithBody(c.Server, contentType, body) if err != nil { @@ -19571,6 +19868,121 @@ func NewPostApiAuthOauth2RegisterRequestWithBody(server string, contentType stri return req, nil } +// NewDeleteDynamicOAuthClientRegistrationRequest generates requests for DeleteDynamicOAuthClientRegistration +func NewDeleteDynamicOAuthClientRegistrationRequest(server string, clientId string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clientId", clientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/auth/oauth2/register/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodDelete, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetDynamicOAuthClientRegistrationRequest generates requests for GetDynamicOAuthClientRegistration +func NewGetDynamicOAuthClientRegistrationRequest(server string, clientId string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clientId", clientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/auth/oauth2/register/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodGet, queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUpdateDynamicOAuthClientRegistrationRequest calls the generic UpdateDynamicOAuthClientRegistration builder with application/json body +func NewUpdateDynamicOAuthClientRegistrationRequest(server string, clientId string, body UpdateDynamicOAuthClientRegistrationJSONRequestBody) (*http.Request, error) { + var bodyReader io.Reader + buf, err := json.Marshal(body) + if err != nil { + return nil, err + } + bodyReader = bytes.NewReader(buf) + return NewUpdateDynamicOAuthClientRegistrationRequestWithBody(server, clientId, "application/json", bodyReader) +} + +// NewUpdateDynamicOAuthClientRegistrationRequestWithBody generates requests for UpdateDynamicOAuthClientRegistration with any type of body +func NewUpdateDynamicOAuthClientRegistrationRequestWithBody(server string, clientId string, contentType string, body io.Reader) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithOptions("simple", false, "clientId", clientId, runtime.StyleParamOptions{ParamLocation: runtime.ParamLocationPath, Type: "string", Format: ""}) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/auth/oauth2/register/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest(http.MethodPut, queryURL.String(), body) + if err != nil { + return nil, err + } + + req.Header.Add("Content-Type", contentType) + + return req, nil +} + // NewPostApiAuthOauth2RevokeRequest calls the generic PostApiAuthOauth2Revoke builder with application/json body func NewPostApiAuthOauth2RevokeRequest(server string, body PostApiAuthOauth2RevokeJSONRequestBody) (*http.Request, error) { var bodyReader io.Reader @@ -28571,6 +28983,17 @@ type ClientWithResponsesInterface interface { PostApiAuthOauth2RegisterWithResponse(ctx context.Context, body PostApiAuthOauth2RegisterJSONRequestBody, reqEditors ...RequestEditorFn) (*PostApiAuthOauth2RegisterResponse, error) + // DeleteDynamicOAuthClientRegistrationWithResponse request + DeleteDynamicOAuthClientRegistrationWithResponse(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*DeleteDynamicOAuthClientRegistrationResponse, error) + + // GetDynamicOAuthClientRegistrationWithResponse request + GetDynamicOAuthClientRegistrationWithResponse(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*GetDynamicOAuthClientRegistrationResponse, error) + + // UpdateDynamicOAuthClientRegistrationWithBodyWithResponse request with any body + UpdateDynamicOAuthClientRegistrationWithBodyWithResponse(ctx context.Context, clientId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateDynamicOAuthClientRegistrationResponse, error) + + UpdateDynamicOAuthClientRegistrationWithResponse(ctx context.Context, clientId string, body UpdateDynamicOAuthClientRegistrationJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateDynamicOAuthClientRegistrationResponse, error) + // PostApiAuthOauth2RevokeWithBodyWithResponse request with any body PostApiAuthOauth2RevokeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthOauth2RevokeResponse, error) @@ -32766,7 +33189,9 @@ type PostApiAuthOauth2RegisterResponse struct { Public *bool `json:"public,omitempty"` // RedirectUris List of allowed redirect uris - RedirectUris *[]string `json:"redirect_uris,omitempty"` + RedirectUris *[]string `json:"redirect_uris,omitempty"` + RegistrationAccessToken string `json:"registration_access_token"` + RegistrationClientUri string `json:"registration_client_uri"` // ResponseTypes Response types the client may use at the authorization endpoint ResponseTypes *[]PostApiAuthOauth2Register201JSONResponseBodyResponseTypes `json:"response_types,omitempty"` @@ -32839,6 +33264,95 @@ func (r PostApiAuthOauth2RegisterResponse) ContentType() string { return "" } +type DeleteDynamicOAuthClientRegistrationResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r DeleteDynamicOAuthClientRegistrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r DeleteDynamicOAuthClientRegistrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r DeleteDynamicOAuthClientRegistrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type GetDynamicOAuthClientRegistrationResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *GetDynamicOAuthClientRegistration200JSONResponseBody +} + +// Status returns HTTPResponse.Status +func (r GetDynamicOAuthClientRegistrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetDynamicOAuthClientRegistrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r GetDynamicOAuthClientRegistrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + +type UpdateDynamicOAuthClientRegistrationResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *UpdateDynamicOAuthClientRegistration200JSONResponseBody +} + +// Status returns HTTPResponse.Status +func (r UpdateDynamicOAuthClientRegistrationResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UpdateDynamicOAuthClientRegistrationResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +// ContentType is a convenience method to retrieve the Content-Type value from the HTTP response headers +func (r UpdateDynamicOAuthClientRegistrationResponse) ContentType() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Header.Get("Content-Type") + } + return "" +} + type PostApiAuthOauth2RevokeResponse struct { Body []byte HTTPResponse *http.Response @@ -41306,6 +41820,41 @@ func (c *ClientWithResponses) PostApiAuthOauth2RegisterWithResponse(ctx context. return ParsePostApiAuthOauth2RegisterResponse(rsp) } +// DeleteDynamicOAuthClientRegistrationWithResponse request returning *DeleteDynamicOAuthClientRegistrationResponse +func (c *ClientWithResponses) DeleteDynamicOAuthClientRegistrationWithResponse(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*DeleteDynamicOAuthClientRegistrationResponse, error) { + rsp, err := c.DeleteDynamicOAuthClientRegistration(ctx, clientId, reqEditors...) + if err != nil { + return nil, err + } + return ParseDeleteDynamicOAuthClientRegistrationResponse(rsp) +} + +// GetDynamicOAuthClientRegistrationWithResponse request returning *GetDynamicOAuthClientRegistrationResponse +func (c *ClientWithResponses) GetDynamicOAuthClientRegistrationWithResponse(ctx context.Context, clientId string, reqEditors ...RequestEditorFn) (*GetDynamicOAuthClientRegistrationResponse, error) { + rsp, err := c.GetDynamicOAuthClientRegistration(ctx, clientId, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetDynamicOAuthClientRegistrationResponse(rsp) +} + +// UpdateDynamicOAuthClientRegistrationWithBodyWithResponse request with arbitrary body returning *UpdateDynamicOAuthClientRegistrationResponse +func (c *ClientWithResponses) UpdateDynamicOAuthClientRegistrationWithBodyWithResponse(ctx context.Context, clientId string, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*UpdateDynamicOAuthClientRegistrationResponse, error) { + rsp, err := c.UpdateDynamicOAuthClientRegistrationWithBody(ctx, clientId, contentType, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateDynamicOAuthClientRegistrationResponse(rsp) +} + +func (c *ClientWithResponses) UpdateDynamicOAuthClientRegistrationWithResponse(ctx context.Context, clientId string, body UpdateDynamicOAuthClientRegistrationJSONRequestBody, reqEditors ...RequestEditorFn) (*UpdateDynamicOAuthClientRegistrationResponse, error) { + rsp, err := c.UpdateDynamicOAuthClientRegistration(ctx, clientId, body, reqEditors...) + if err != nil { + return nil, err + } + return ParseUpdateDynamicOAuthClientRegistrationResponse(rsp) +} + // PostApiAuthOauth2RevokeWithBodyWithResponse request with arbitrary body returning *PostApiAuthOauth2RevokeResponse func (c *ClientWithResponses) PostApiAuthOauth2RevokeWithBodyWithResponse(ctx context.Context, contentType string, body io.Reader, reqEditors ...RequestEditorFn) (*PostApiAuthOauth2RevokeResponse, error) { rsp, err := c.PostApiAuthOauth2RevokeWithBody(ctx, contentType, body, reqEditors...) @@ -49014,7 +49563,9 @@ func ParsePostApiAuthOauth2RegisterResponse(rsp *http.Response) (*PostApiAuthOau Public *bool `json:"public,omitempty"` // RedirectUris List of allowed redirect uris - RedirectUris *[]string `json:"redirect_uris,omitempty"` + RedirectUris *[]string `json:"redirect_uris,omitempty"` + RegistrationAccessToken string `json:"registration_access_token"` + RegistrationClientUri string `json:"registration_client_uri"` // ResponseTypes Response types the client may use at the authorization endpoint ResponseTypes *[]PostApiAuthOauth2Register201JSONResponseBodyResponseTypes `json:"response_types,omitempty"` @@ -49107,6 +49658,74 @@ func ParsePostApiAuthOauth2RegisterResponse(rsp *http.Response) (*PostApiAuthOau return response, nil } +// ParseDeleteDynamicOAuthClientRegistrationResponse parses an HTTP response from a DeleteDynamicOAuthClientRegistrationWithResponse call +func ParseDeleteDynamicOAuthClientRegistrationResponse(rsp *http.Response) (*DeleteDynamicOAuthClientRegistrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &DeleteDynamicOAuthClientRegistrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseGetDynamicOAuthClientRegistrationResponse parses an HTTP response from a GetDynamicOAuthClientRegistrationWithResponse call +func ParseGetDynamicOAuthClientRegistrationResponse(rsp *http.Response) (*GetDynamicOAuthClientRegistrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetDynamicOAuthClientRegistrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest GetDynamicOAuthClientRegistration200JSONResponseBody + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + +// ParseUpdateDynamicOAuthClientRegistrationResponse parses an HTTP response from a UpdateDynamicOAuthClientRegistrationWithResponse call +func ParseUpdateDynamicOAuthClientRegistrationResponse(rsp *http.Response) (*UpdateDynamicOAuthClientRegistrationResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UpdateDynamicOAuthClientRegistrationResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest UpdateDynamicOAuthClientRegistration200JSONResponseBody + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + } + + return response, nil +} + // ParsePostApiAuthOauth2RevokeResponse parses an HTTP response from a PostApiAuthOauth2RevokeWithResponse call func ParsePostApiAuthOauth2RevokeResponse(rsp *http.Response) (*PostApiAuthOauth2RevokeResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/docs/design/oauth-server.md b/docs/design/oauth-server.md index 0a6a25e3..79b744f6 100644 --- a/docs/design/oauth-server.md +++ b/docs/design/oauth-server.md @@ -32,6 +32,7 @@ Starting from `https://zpan.example/api`, clients discover: | Protected resource metadata | `/.well-known/oauth-protected-resource/api` | | Authorization server metadata | `/.well-known/oauth-authorization-server/api/auth` | | Dynamic client registration | `/api/auth/oauth2/register` | +| Dynamic client registration management (RFC 7592) | URI returned as `registration_client_uri` | | Pushed authorization requests | `/api/auth/oauth2/par` | Authorization-server metadata advertises `scopes_supported`, @@ -41,12 +42,23 @@ catalog endpoint. RFC 7591 clients can register `authorization_details_types`; ZPan persists and echoes supported values and rejects unknown types as invalid client metadata. +New dynamic registrations also receive an opaque `registration_access_token` +and a client-specific `registration_client_uri`. The token is stored only as a +hash and authenticates RFC 7592 `GET`, full-replacement `PUT`, and `DELETE` +operations. Configuration reads and updates never return the OAuth +`client_secret`; the secret is returned only when initially issued. Clients +registered before RFC 7592 support remain valid but do not gain a management +credential retroactively. A controller that needs to change such a registration +creates a new registration generation and leaves existing connections pinned to +their original client identity until they are reconnected. + OpenAPI uses standard `security` declarations. Every protected ZPan operation declares its OAuth scopes, plus cookie and bearer alternatives. Role constraints that OpenAPI cannot express use the narrow `x-zpan-authorization-constraints` extension. Better Auth operations and their -generated OpenAPI definitions remain owned by Better Auth and are not rewritten -by ZPan. +generated OpenAPI definitions remain owned by Better Auth. ZPan augments the +dynamic-registration response and adds the RFC 7592 configuration endpoint that +is implemented at its auth boundary. ## Workspace Authorization Details diff --git a/migrations/0091_oauth-client-registration-management.sql b/migrations/0091_oauth-client-registration-management.sql new file mode 100644 index 00000000..5a9b31f7 --- /dev/null +++ b/migrations/0091_oauth-client-registration-management.sql @@ -0,0 +1,10 @@ +CREATE TABLE `oauthClientRegistration` ( + `client_id` text PRIMARY KEY NOT NULL, + `token_hash` text NOT NULL, + `created_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL, + `updated_at` integer DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) NOT NULL, + FOREIGN KEY (`client_id`) REFERENCES `oauthClient`(`client_id`) ON UPDATE no action ON DELETE cascade +); +--> statement-breakpoint +CREATE UNIQUE INDEX `oauthClientRegistration_token_hash_unique` ON `oauthClientRegistration` (`token_hash`);--> statement-breakpoint +CREATE INDEX `oauthClientRegistration_token_hash_idx` ON `oauthClientRegistration` (`token_hash`); \ No newline at end of file diff --git a/migrations/meta/0091_snapshot.json b/migrations/meta/0091_snapshot.json new file mode 100644 index 00000000..17421e5b --- /dev/null +++ b/migrations/meta/0091_snapshot.json @@ -0,0 +1,6398 @@ +{ + "version": "6", + "dialect": "sqlite", + "id": "a6d82a35-7f29-4625-97bf-7ced6da53537", + "prevId": "9ba2237d-36aa-4c5b-8243-f69c62556508", + "tables": { + "announcements": { + "name": "announcements", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "published_at": { + "name": "published_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "announcements_status_priority_idx": { + "name": "announcements_status_priority_idx", + "columns": [ + "status", + "priority" + ], + "isUnique": false + }, + "announcements_published_idx": { + "name": "announcements_published_idx", + "columns": [ + "published_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "audit_events": { + "name": "audit_events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_type": { + "name": "target_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_id": { + "name": "target_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_name": { + "name": "target_name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "actor_type": { + "name": "actor_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actor_ref": { + "name": "actor_ref", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "actor_issuer": { + "name": "actor_issuer", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "audit_events_org_created_idx": { + "name": "audit_events_org_created_idx", + "columns": [ + "org_id", + "created_at" + ], + "isUnique": false + }, + "audit_events_user_created_idx": { + "name": "audit_events_user_created_idx", + "columns": [ + "user_id", + "created_at" + ], + "isUnique": false + }, + "audit_events_action_created_idx": { + "name": "audit_events_action_created_idx", + "columns": [ + "action", + "created_at" + ], + "isUnique": false + }, + "audit_events_target_created_idx": { + "name": "audit_events_target_created_idx", + "columns": [ + "target_type", + "target_id", + "created_at" + ], + "isUnique": false + }, + "audit_events_created_idx": { + "name": "audit_events_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "background_jobs": { + "name": "background_jobs", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "target_folder": { + "name": "target_folder", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_path": { + "name": "target_path", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "input_bytes": { + "name": "input_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "output_bytes": { + "name": "output_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "processed_bytes": { + "name": "processed_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "file_count": { + "name": "file_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "current_filename": { + "name": "current_filename", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result_metadata": { + "name": "result_metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "retryable": { + "name": "retryable", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "cancelable": { + "name": "cancelable", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "retried_from_job_id": { + "name": "retried_from_job_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "finished_at": { + "name": "finished_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "background_jobs_org_created_idx": { + "name": "background_jobs_org_created_idx", + "columns": [ + "org_id", + "created_at" + ], + "isUnique": false + }, + "background_jobs_org_status_idx": { + "name": "background_jobs_org_status_idx", + "columns": [ + "org_id", + "status" + ], + "isUnique": false + }, + "background_jobs_org_type_idx": { + "name": "background_jobs_org_type_idx", + "columns": [ + "org_id", + "type" + ], + "isUnique": false + }, + "background_jobs_created_idx": { + "name": "background_jobs_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "background_jobs_org_page_idx": { + "name": "background_jobs_org_page_idx", + "columns": [ + "org_id", + "created_at", + "id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "cloud_traffic_reports": { + "name": "cloud_traffic_reports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "period": { + "name": "period", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "storage_id": { + "name": "storage_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "unit_bytes": { + "name": "unit_bytes", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "credits_per_unit": { + "name": "credits_per_unit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "next_retry_at": { + "name": "next_retry_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "issued_at": { + "name": "issued_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "cloud_traffic_reports_event_uniq": { + "name": "cloud_traffic_reports_event_uniq", + "columns": [ + "event_id" + ], + "isUnique": true + }, + "cloud_traffic_reports_org_period_idx": { + "name": "cloud_traffic_reports_org_period_idx", + "columns": [ + "org_id", + "period" + ], + "isUnique": false + }, + "cloud_traffic_reports_status_idx": { + "name": "cloud_traffic_reports_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "cloud_traffic_reports_retry_idx": { + "name": "cloud_traffic_reports_retry_idx", + "columns": [ + "status", + "next_retry_at", + "created_at" + ], + "isUnique": false + }, + "cloud_traffic_reports_issued_idx": { + "name": "cloud_traffic_reports_issued_idx", + "columns": [ + "issued_at" + ], + "isUnique": false + }, + "cloud_traffic_reports_updated_idx": { + "name": "cloud_traffic_reports_updated_idx", + "columns": [ + "updated_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "download_tasks": { + "name": "download_tasks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_type": { + "name": "source_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_uri": { + "name": "source_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "display_name": { + "name": "display_name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "target_folder": { + "name": "target_folder", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tags": { + "name": "tags", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "assigned_downloader_id": { + "name": "assigned_downloader_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "attempt": { + "name": "attempt", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "billing_authorized_bytes": { + "name": "billing_authorized_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "billing_charged_bytes": { + "name": "billing_charged_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "billing_charged_credits": { + "name": "billing_charged_credits", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "billing_status": { + "name": "billing_status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'none'" + }, + "error_code": { + "name": "error_code", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "error_message": { + "name": "error_message", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "result_object_id": { + "name": "result_object_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "runtime": { + "name": "runtime", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "events": { + "name": "events", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "resolve_started_at": { + "name": "resolve_started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resolve_completed_at": { + "name": "resolve_completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "download_completed_at": { + "name": "download_completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ingest_started_at": { + "name": "ingest_started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ingest_completed_at": { + "name": "ingest_completed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "seeding_started_at": { + "name": "seeding_started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "seeding_stopped_at": { + "name": "seeding_stopped_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "assigned_at": { + "name": "assigned_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "started_at": { + "name": "started_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "finished_at": { + "name": "finished_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "deleted_at": { + "name": "deleted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "download_tasks_org_created_idx": { + "name": "download_tasks_org_created_idx", + "columns": [ + "org_id", + "created_at" + ], + "isUnique": false + }, + "download_tasks_org_status_idx": { + "name": "download_tasks_org_status_idx", + "columns": [ + "org_id", + "status" + ], + "isUnique": false + }, + "download_tasks_org_category_idx": { + "name": "download_tasks_org_category_idx", + "columns": [ + "org_id", + "category" + ], + "isUnique": false + }, + "download_tasks_org_tags_idx": { + "name": "download_tasks_org_tags_idx", + "columns": [ + "org_id", + "tags" + ], + "isUnique": false + }, + "download_tasks_downloader_idx": { + "name": "download_tasks_downloader_idx", + "columns": [ + "assigned_downloader_id", + "status" + ], + "isUnique": false + }, + "download_tasks_created_idx": { + "name": "download_tasks_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "download_tasks_finished_idx": { + "name": "download_tasks_finished_idx", + "columns": [ + "finished_at" + ], + "isUnique": false + }, + "download_tasks_org_deleted_created_idx": { + "name": "download_tasks_org_deleted_created_idx", + "columns": [ + "org_id", + "deleted_at", + "created_at" + ], + "isUnique": false + }, + "download_tasks_org_page_idx": { + "name": "download_tasks_org_page_idx", + "columns": [ + "org_id", + "deleted_at", + "created_at", + "id" + ], + "isUnique": false + }, + "download_tasks_downloader_page_idx": { + "name": "download_tasks_downloader_page_idx", + "columns": [ + "assigned_downloader_id", + "deleted_at", + "created_at", + "id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "downloaders": { + "name": "downloaders", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token_jti": { + "name": "token_jti", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'offline'" + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unknown'" + }, + "hostname": { + "name": "hostname", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unknown'" + }, + "platform": { + "name": "platform", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unknown'" + }, + "arch": { + "name": "arch", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unknown'" + }, + "engine": { + "name": "engine", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'http'" + }, + "capabilities": { + "name": "capabilities", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'[]'" + }, + "max_concurrent_tasks": { + "name": "max_concurrent_tasks", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "current_tasks": { + "name": "current_tasks", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "download_bps": { + "name": "download_bps", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "upload_bps": { + "name": "upload_bps", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "free_disk_bytes": { + "name": "free_disk_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "remote_download_credit_billing_enabled": { + "name": "remote_download_credit_billing_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "remote_download_credit_unit_bytes": { + "name": "remote_download_credit_unit_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 104857600 + }, + "remote_download_credit_per_unit": { + "name": "remote_download_credit_per_unit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "last_heartbeat_at": { + "name": "last_heartbeat_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "downloaders_token_jti_unique": { + "name": "downloaders_token_jti_unique", + "columns": [ + "token_jti" + ], + "isUnique": true + }, + "downloaders_status_idx": { + "name": "downloaders_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "downloaders_enabled_idx": { + "name": "downloaders_enabled_idx", + "columns": [ + "enabled" + ], + "isUnique": false + }, + "downloaders_created_idx": { + "name": "downloaders_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "image_hosting_configs": { + "name": "image_hosting_configs", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "custom_domain": { + "name": "custom_domain", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "domain_provider": { + "name": "domain_provider", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "provider_hostname_id": { + "name": "provider_hostname_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "domain_status": { + "name": "domain_status", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "domain_error": { + "name": "domain_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "verification_token": { + "name": "verification_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "domain_last_checked_at": { + "name": "domain_last_checked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "domain_verified_at": { + "name": "domain_verified_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "referer_allowlist": { + "name": "referer_allowlist", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "image_hosting_configs_custom_domain_unique": { + "name": "image_hosting_configs_custom_domain_unique", + "columns": [ + "custom_domain" + ], + "isUnique": true + } + }, + "foreignKeys": { + "image_hosting_configs_org_id_organization_id_fk": { + "name": "image_hosting_configs_org_id_organization_id_fk", + "tableFrom": "image_hosting_configs", + "tableTo": "organization", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "image_hostings": { + "name": "image_hostings", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "path": { + "name": "path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "storage_id": { + "name": "storage_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "mime": { + "name": "mime", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "width": { + "name": "width", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "height": { + "name": "height", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "purged_at": { + "name": "purged_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_count": { + "name": "access_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "last_accessed_at": { + "name": "last_accessed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "image_hostings_token_unique": { + "name": "image_hostings_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "image_hostings_org_path_uniq": { + "name": "image_hostings_org_path_uniq", + "columns": [ + "org_id", + "path" + ], + "isUnique": true, + "where": "\"image_hostings\".\"purged_at\" IS NULL" + }, + "image_hostings_org_created_idx": { + "name": "image_hostings_org_created_idx", + "columns": [ + "org_id", + "created_at" + ], + "isUnique": false + }, + "image_hostings_page_idx": { + "name": "image_hostings_page_idx", + "columns": [ + "org_id", + "status", + "purged_at", + "created_at", + "id" + ], + "isUnique": false + }, + "image_hostings_token_idx": { + "name": "image_hostings_token_idx", + "columns": [ + "token" + ], + "isUnique": false + } + }, + "foreignKeys": { + "image_hostings_org_id_organization_id_fk": { + "name": "image_hostings_org_id_organization_id_fk", + "tableFrom": "image_hostings", + "tableTo": "organization", + "columnsFrom": [ + "org_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "image_hostings_storage_id_storages_id_fk": { + "name": "image_hostings_storage_id_storages_id_fk", + "tableFrom": "image_hostings", + "tableTo": "storages", + "columnsFrom": [ + "storage_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invite_codes": { + "name": "invite_codes", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "code": { + "name": "code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "used_by": { + "name": "used_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "used_at": { + "name": "used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "invite_codes_code_unique": { + "name": "invite_codes_code_unique", + "columns": [ + "code" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "license_bindings": { + "name": "license_bindings", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "cloud_binding_id": { + "name": "cloud_binding_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cloud_store_id": { + "name": "cloud_store_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "instance_id": { + "name": "instance_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cloud_account_id": { + "name": "cloud_account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cloud_account_email": { + "name": "cloud_account_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cached_certificate": { + "name": "cached_certificate", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cached_certificate_expires_at": { + "name": "cached_certificate_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "bound_at": { + "name": "bound_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "disconnected_at": { + "name": "disconnected_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_refresh_at": { + "name": "last_refresh_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_refresh_error": { + "name": "last_refresh_error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "license_bindings_active_uniq": { + "name": "license_bindings_active_uniq", + "columns": [ + "status" + ], + "isUnique": true, + "where": "status = 'active'" + }, + "license_bindings_cloud_binding_idx": { + "name": "license_bindings_cloud_binding_idx", + "columns": [ + "cloud_binding_id" + ], + "isUnique": false + }, + "license_bindings_instance_idx": { + "name": "license_bindings_instance_idx", + "columns": [ + "instance_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "matters": { + "name": "matters", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "alias": { + "name": "alias", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "size": { + "name": "size", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "dirtype": { + "name": "dirtype", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 0 + }, + "parent": { + "name": "parent", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "object": { + "name": "object", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "storage_id": { + "name": "storage_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'draft'" + }, + "trashed_at": { + "name": "trashed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "purged_at": { + "name": "purged_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "matters_alias_unique": { + "name": "matters_alias_unique", + "columns": [ + "alias" + ], + "isUnique": true + }, + "matters_status_dir_created_idx": { + "name": "matters_status_dir_created_idx", + "columns": [ + "status", + "dirtype", + "created_at" + ], + "isUnique": false + }, + "matters_webdav_path_idx": { + "name": "matters_webdav_path_idx", + "columns": [ + "org_id", + "parent", + "name", + "status", + "trashed_at", + "purged_at" + ], + "isUnique": false + }, + "matters_webdav_children_idx": { + "name": "matters_webdav_children_idx", + "columns": [ + "org_id", + "parent", + "status", + "trashed_at", + "purged_at", + "\"dirtype\" desc", + "name" + ], + "isUnique": false + }, + "matters_list_page_idx": { + "name": "matters_list_page_idx", + "columns": [ + "org_id", + "parent", + "status", + "trashed_at", + "purged_at", + "\"dirtype\" desc", + "created_at", + "id" + ], + "isUnique": false + }, + "matters_trash_page_idx": { + "name": "matters_trash_page_idx", + "columns": [ + "org_id", + "status", + "purged_at", + "trashed_at", + "created_at", + "id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "notifications": { + "name": "notifications", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "title": { + "name": "title", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "body": { + "name": "body", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "ref_type": { + "name": "ref_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ref_id": { + "name": "ref_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "read_at": { + "name": "read_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "notifications_user_created_idx": { + "name": "notifications_user_created_idx", + "columns": [ + "user_id", + "created_at" + ], + "isUnique": false + }, + "notifications_user_read_idx": { + "name": "notifications_user_read_idx", + "columns": [ + "user_id", + "read_at" + ], + "isUnique": false + }, + "notifications_user_page_idx": { + "name": "notifications_user_page_idx", + "columns": [ + "user_id", + "read_at", + "created_at", + "id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "object_upload_sessions": { + "name": "object_upload_sessions", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "object_id": { + "name": "object_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "storage_id": { + "name": "storage_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "storage_key": { + "name": "storage_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "upload_id": { + "name": "upload_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "part_size": { + "name": "part_size", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "on_conflict": { + "name": "on_conflict", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'fail'" + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_by": { + "name": "created_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "object_upload_sessions_object_idx": { + "name": "object_upload_sessions_object_idx", + "columns": [ + "org_id", + "object_id" + ], + "isUnique": false + }, + "object_upload_sessions_expires_idx": { + "name": "object_upload_sessions_expires_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "org_quota_entitlements": { + "name": "org_quota_entitlements", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "entitlement_type": { + "name": "entitlement_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'grant'" + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "source_id": { + "name": "source_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "starts_at": { + "name": "starts_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "org_quota_entitlements_org_resource_idx": { + "name": "org_quota_entitlements_org_resource_idx", + "columns": [ + "org_id", + "resource_type", + "status" + ], + "isUnique": false + }, + "org_quota_entitlements_org_type_idx": { + "name": "org_quota_entitlements_org_type_idx", + "columns": [ + "org_id", + "resource_type", + "entitlement_type", + "status" + ], + "isUnique": false + }, + "org_quota_entitlements_source_resource_uniq": { + "name": "org_quota_entitlements_source_resource_uniq", + "columns": [ + "source", + "source_id", + "resource_type" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "org_quotas": { + "name": "org_quotas", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "quota": { + "name": "quota", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "traffic_quota": { + "name": "traffic_quota", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "traffic_used": { + "name": "traffic_used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "traffic_period": { + "name": "traffic_period", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'1970-01'" + } + }, + "indexes": { + "org_quotas_org_uniq": { + "name": "org_quotas_org_uniq", + "columns": [ + "org_id" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "remote_download_usage_reports": { + "name": "remote_download_usage_reports", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "downloader_id": { + "name": "downloader_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "task_id": { + "name": "task_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit_index": { + "name": "unit_index", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "unit_bytes": { + "name": "unit_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "credits_per_unit": { + "name": "credits_per_unit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "remote_download_usage_reports_event_id_unique": { + "name": "remote_download_usage_reports_event_id_unique", + "columns": [ + "event_id" + ], + "isUnique": true + }, + "remote_download_usage_task_unit_uniq": { + "name": "remote_download_usage_task_unit_uniq", + "columns": [ + "task_id", + "unit_index" + ], + "isUnique": true + }, + "remote_download_usage_org_idx": { + "name": "remote_download_usage_org_idx", + "columns": [ + "org_id" + ], + "isUnique": false + }, + "remote_download_usage_status_idx": { + "name": "remote_download_usage_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "remote_download_usage_created_idx": { + "name": "remote_download_usage_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "resource_changes": { + "name": "resource_changes", + "columns": { + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": true, + "notNull": true, + "autoincrement": true + }, + "scope_type": { + "name": "scope_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "change_type": { + "name": "change_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "resource_changes_scope_sequence_idx": { + "name": "resource_changes_scope_sequence_idx", + "columns": [ + "scope_type", + "scope_id", + "sequence" + ], + "isUnique": false + }, + "resource_changes_resource_sequence_idx": { + "name": "resource_changes_resource_sequence_idx", + "columns": [ + "resource_type", + "resource_id", + "sequence" + ], + "isUnique": false + }, + "resource_changes_occurred_idx": { + "name": "resource_changes_occurred_idx", + "columns": [ + "occurred_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "share_recipients": { + "name": "share_recipients", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "share_id": { + "name": "share_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "recipient_user_id": { + "name": "recipient_user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "recipient_email": { + "name": "recipient_email", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "share_recipients_share_id_idx": { + "name": "share_recipients_share_id_idx", + "columns": [ + "share_id" + ], + "isUnique": false + }, + "share_recipients_user_id_idx": { + "name": "share_recipients_user_id_idx", + "columns": [ + "recipient_user_id" + ], + "isUnique": false + }, + "share_recipients_email_idx": { + "name": "share_recipients_email_idx", + "columns": [ + "recipient_email" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "shares": { + "name": "shares", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "matter_id": { + "name": "matter_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "creator_id": { + "name": "creator_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "download_limit": { + "name": "download_limit", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "views": { + "name": "views", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "downloads": { + "name": "downloads", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'active'" + }, + "private": { + "name": "private", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "shares_token_unique": { + "name": "shares_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "shares_creator_status_created_idx": { + "name": "shares_creator_status_created_idx", + "columns": [ + "creator_id", + "status", + "created_at", + "id" + ], + "isUnique": false + }, + "shares_creator_private_created_idx": { + "name": "shares_creator_private_created_idx", + "columns": [ + "creator_id", + "private", + "created_at" + ], + "isUnique": false + }, + "shares_created_idx": { + "name": "shares_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "site_invitations": { + "name": "site_invitations", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "invited_by": { + "name": "invited_by", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "accepted_by": { + "name": "accepted_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "accepted_at": { + "name": "accepted_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_by": { + "name": "revoked_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "site_invitations_token_unique": { + "name": "site_invitations_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "site_invitations_email_idx": { + "name": "site_invitations_email_idx", + "columns": [ + "email" + ], + "isUnique": false + }, + "site_invitations_created_idx": { + "name": "site_invitations_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "site_invitations_expires_idx": { + "name": "site_invitations_expires_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "stats_rollups_hourly": { + "name": "stats_rollups_hourly", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "bucket_start": { + "name": "bucket_start", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "metric_key": { + "name": "metric_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "dimension_key": { + "name": "dimension_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "dimension_value": { + "name": "dimension_value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "count": { + "name": "count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "unique_count": { + "name": "unique_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "stats_rollups_hourly_bucket_metric_dim_uniq": { + "name": "stats_rollups_hourly_bucket_metric_dim_uniq", + "columns": [ + "bucket_start", + "org_id", + "metric_key", + "dimension_key", + "dimension_value" + ], + "isUnique": true + }, + "stats_rollups_hourly_metric_bucket_idx": { + "name": "stats_rollups_hourly_metric_bucket_idx", + "columns": [ + "metric_key", + "bucket_start" + ], + "isUnique": false + }, + "stats_rollups_hourly_dimension_bucket_idx": { + "name": "stats_rollups_hourly_dimension_bucket_idx", + "columns": [ + "metric_key", + "dimension_key", + "bucket_start" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "storage_usage_breakdowns": { + "name": "storage_usage_breakdowns", + "columns": { + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "category": { + "name": "category", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "bytes": { + "name": "bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "file_count": { + "name": "file_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "storage_usage_breakdowns_org_category_uniq": { + "name": "storage_usage_breakdowns_org_category_uniq", + "columns": [ + "org_id", + "category" + ], + "isUnique": true + }, + "storage_usage_breakdowns_org_idx": { + "name": "storage_usage_breakdowns_org_idx", + "columns": [ + "org_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "storage_usage_ledger": { + "name": "storage_usage_ledger", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "event_key": { + "name": "event_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "storage_id": { + "name": "storage_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_type": { + "name": "resource_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "delta_bytes": { + "name": "delta_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "occurred_at": { + "name": "occurred_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "storage_usage_ledger_event_key_unique": { + "name": "storage_usage_ledger_event_key_unique", + "columns": [ + "event_key" + ], + "isUnique": true + }, + "storage_usage_ledger_occurred_idx": { + "name": "storage_usage_ledger_occurred_idx", + "columns": [ + "occurred_at" + ], + "isUnique": false + }, + "storage_usage_ledger_org_occurred_idx": { + "name": "storage_usage_ledger_org_occurred_idx", + "columns": [ + "org_id", + "occurred_at" + ], + "isUnique": false + }, + "storage_usage_ledger_storage_occurred_idx": { + "name": "storage_usage_ledger_storage_occurred_idx", + "columns": [ + "storage_id", + "occurred_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "storages": { + "name": "storages", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "provider": { + "name": "provider", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "bucket": { + "name": "bucket", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "endpoint": { + "name": "endpoint", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "region": { + "name": "region", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'auto'" + }, + "access_key": { + "name": "access_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "secret_key": { + "name": "secret_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "file_path": { + "name": "file_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "custom_host": { + "name": "custom_host", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "''" + }, + "capacity": { + "name": "capacity", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "egress_credit_billing_enabled": { + "name": "egress_credit_billing_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "egress_credit_unit_bytes": { + "name": "egress_credit_unit_bytes", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 104857600 + }, + "egress_credit_per_unit": { + "name": "egress_credit_per_unit", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 1 + }, + "force_path_style": { + "name": "force_path_style", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "used": { + "name": "used", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'unknown'" + }, + "status_reason": { + "name": "status_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status_checked_at": { + "name": "status_checked_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "system_options": { + "name": "system_options", + "columns": { + "key": { + "name": "key", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "team_invite_links": { + "name": "team_invite_links", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "team_invite_links_token_unique": { + "name": "team_invite_links_token_unique", + "columns": [ + "token" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "webdav_dead_properties": { + "name": "webdav_dead_properties", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_path": { + "name": "resource_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "namespace": { + "name": "namespace", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "webdav_dead_properties_resource_prop_uniq": { + "name": "webdav_dead_properties_resource_prop_uniq", + "columns": [ + "org_id", + "resource_path", + "namespace", + "name" + ], + "isUnique": true + }, + "webdav_dead_properties_resource_idx": { + "name": "webdav_dead_properties_resource_idx", + "columns": [ + "org_id", + "resource_path" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "webdav_locks": { + "name": "webdav_locks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_path": { + "name": "resource_path", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "owner": { + "name": "owner", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "depth": { + "name": "depth", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'infinity'" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "webdav_locks_token_unique": { + "name": "webdav_locks_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "webdav_locks_resource_idx": { + "name": "webdav_locks_resource_idx", + "columns": [ + "org_id", + "resource_path" + ], + "isUnique": false + }, + "webdav_locks_expires_idx": { + "name": "webdav_locks_expires_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "webhook_events": { + "name": "webhook_events", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "source": { + "name": "source", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'cloud'" + }, + "event_id": { + "name": "event_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "event_type": { + "name": "event_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'order.quota_changed'" + }, + "payload_hash": { + "name": "payload_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "raw_payload": { + "name": "raw_payload", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "error": { + "name": "error", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "processed_at": { + "name": "processed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "webhook_events_source_event_uniq": { + "name": "webhook_events_source_event_uniq", + "columns": [ + "source", + "event_id" + ], + "isUnique": true + }, + "webhook_events_source_created_idx": { + "name": "webhook_events_source_created_idx", + "columns": [ + "source", + "created_at" + ], + "isUnique": false + }, + "webhook_events_status_idx": { + "name": "webhook_events_status_idx", + "columns": [ + "status" + ], + "isUnique": false + }, + "webhook_events_processed_idx": { + "name": "webhook_events_processed_idx", + "columns": [ + "processed_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "x402_capacity_purchase_intents": { + "name": "x402_capacity_purchase_intents", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "org_id": { + "name": "org_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "request_hash": { + "name": "request_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "idempotency_key": { + "name": "idempotency_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "cloud_order_id": { + "name": "cloud_order_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "cloud_attempt_id": { + "name": "cloud_attempt_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'created'" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "x402_capacity_purchase_intents_org_request_uniq": { + "name": "x402_capacity_purchase_intents_org_request_uniq", + "columns": [ + "org_id", + "resource_id", + "request_hash" + ], + "isUnique": true + }, + "x402_capacity_purchase_intents_org_idempotency_uniq": { + "name": "x402_capacity_purchase_intents_org_idempotency_uniq", + "columns": [ + "org_id", + "idempotency_key" + ], + "isUnique": true + }, + "x402_capacity_purchase_intents_attempt_idx": { + "name": "x402_capacity_purchase_intents_attempt_idx", + "columns": [ + "cloud_attempt_id" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "account": { + "name": "account", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "issuer": { + "name": "issuer", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "''" + }, + "account_id": { + "name": "account_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "provider_id": { + "name": "provider_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "account_issuer_providerAccountId_unique": { + "name": "account_issuer_providerAccountId_unique", + "columns": [ + "issuer", + "account_id" + ], + "isUnique": true + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "apikey": { + "name": "apikey", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "config_id": { + "name": "config_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'default'" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "start": { + "name": "start", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "prefix": { + "name": "prefix", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "refill_interval": { + "name": "refill_interval", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refill_amount": { + "name": "refill_amount", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_refill_at": { + "name": "last_refill_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "enabled": { + "name": "enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "rate_limit_enabled": { + "name": "rate_limit_enabled", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": true + }, + "rate_limit_time_window": { + "name": "rate_limit_time_window", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rate_limit_max": { + "name": "rate_limit_max", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "request_count": { + "name": "request_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": 0 + }, + "remaining": { + "name": "remaining", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_request": { + "name": "last_request", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "permissions": { + "name": "permissions", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "apikey_config_id_idx": { + "name": "apikey_config_id_idx", + "columns": [ + "config_id" + ], + "isUnique": false + }, + "apikey_reference_id_idx": { + "name": "apikey_reference_id_idx", + "columns": [ + "reference_id" + ], + "isUnique": false + }, + "apikey_key_idx": { + "name": "apikey_key_idx", + "columns": [ + "key" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "deviceCode": { + "name": "deviceCode", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "device_code": { + "name": "device_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_code": { + "name": "user_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "last_polled_at": { + "name": "last_polled_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "polling_interval": { + "name": "polling_interval", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "deviceCode_device_code_idx": { + "name": "deviceCode_device_code_idx", + "columns": [ + "device_code" + ], + "isUnique": false + }, + "deviceCode_user_code_idx": { + "name": "deviceCode_user_code_idx", + "columns": [ + "user_code" + ], + "isUnique": false + }, + "deviceCode_status_idx": { + "name": "deviceCode_status_idx", + "columns": [ + "status" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "downloader_bootstrap_credentials": { + "name": "downloader_bootstrap_credentials", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "device_code": { + "name": "device_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "consumed_at": { + "name": "consumed_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "downloader_bootstrap_credentials_token_hash_unique": { + "name": "downloader_bootstrap_credentials_token_hash_unique", + "columns": [ + "token_hash" + ], + "isUnique": true + }, + "downloader_bootstrap_token_hash_idx": { + "name": "downloader_bootstrap_token_hash_idx", + "columns": [ + "token_hash" + ], + "isUnique": false + }, + "downloader_bootstrap_user_idx": { + "name": "downloader_bootstrap_user_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "downloader_bootstrap_consumed_idx": { + "name": "downloader_bootstrap_consumed_idx", + "columns": [ + "consumed_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "downloader_bootstrap_credentials_user_id_user_id_fk": { + "name": "downloader_bootstrap_credentials_user_id_user_id_fk", + "tableFrom": "downloader_bootstrap_credentials", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "invitation": { + "name": "invitation", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'pending'" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "inviter_id": { + "name": "inviter_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "invitation_organizationId_idx": { + "name": "invitation_organizationId_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "invitation_email_idx": { + "name": "invitation_email_idx", + "columns": [ + "email" + ], + "isUnique": false + } + }, + "foreignKeys": { + "invitation_organization_id_organization_id_fk": { + "name": "invitation_organization_id_organization_id_fk", + "tableFrom": "invitation", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "invitation_inviter_id_user_id_fk": { + "name": "invitation_inviter_id_user_id_fk", + "tableFrom": "invitation", + "tableTo": "user", + "columnsFrom": [ + "inviter_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "jwks": { + "name": "jwks", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "public_key": { + "name": "public_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "private_key": { + "name": "private_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "alg": { + "name": "alg", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "crv": { + "name": "crv", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "member": { + "name": "member", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "organization_id": { + "name": "organization_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "'member'" + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "member_organizationId_idx": { + "name": "member_organizationId_idx", + "columns": [ + "organization_id" + ], + "isUnique": false + }, + "member_userId_idx": { + "name": "member_userId_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "member_organization_id_organization_id_fk": { + "name": "member_organization_id_organization_id_fk", + "tableFrom": "member", + "tableTo": "organization", + "columnsFrom": [ + "organization_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "member_user_id_user_id_fk": { + "name": "member_user_id_user_id_fk", + "tableFrom": "member", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthAccessToken": { + "name": "oauthAccessToken", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resources": { + "name": "resources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authorization_details": { + "name": "authorization_details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_id": { + "name": "refresh_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "revoked": { + "name": "revoked", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "confirmation": { + "name": "confirmation", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "oauthAccessToken_token_unique": { + "name": "oauthAccessToken_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "oauthAccessToken_client_id_idx": { + "name": "oauthAccessToken_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "oauthAccessToken_session_id_idx": { + "name": "oauthAccessToken_session_id_idx", + "columns": [ + "session_id" + ], + "isUnique": false + }, + "oauthAccessToken_user_id_idx": { + "name": "oauthAccessToken_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "oauthAccessToken_refresh_id_idx": { + "name": "oauthAccessToken_refresh_id_idx", + "columns": [ + "refresh_id" + ], + "isUnique": false + }, + "oauthAccessToken_token_idx": { + "name": "oauthAccessToken_token_idx", + "columns": [ + "token" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauthAccessToken_client_id_oauthClient_client_id_fk": { + "name": "oauthAccessToken_client_id_oauthClient_client_id_fk", + "tableFrom": "oauthAccessToken", + "tableTo": "oauthClient", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauthAccessToken_session_id_session_id_fk": { + "name": "oauthAccessToken_session_id_session_id_fk", + "tableFrom": "oauthAccessToken", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauthAccessToken_user_id_user_id_fk": { + "name": "oauthAccessToken_user_id_user_id_fk", + "tableFrom": "oauthAccessToken", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauthAccessToken_refresh_id_oauthRefreshToken_id_fk": { + "name": "oauthAccessToken_refresh_id_oauthRefreshToken_id_fk", + "tableFrom": "oauthAccessToken", + "tableTo": "oauthRefreshToken", + "columnsFrom": [ + "refresh_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthClient": { + "name": "oauthClient", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_secret": { + "name": "client_secret", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "skip_consent": { + "name": "skip_consent", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "enable_end_session": { + "name": "enable_end_session", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "subject_type": { + "name": "subject_type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "uri": { + "name": "uri", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "icon": { + "name": "icon", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "contacts": { + "name": "contacts", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "tos": { + "name": "tos", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "policy": { + "name": "policy", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "software_id": { + "name": "software_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "software_version": { + "name": "software_version", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "software_statement": { + "name": "software_statement", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "redirect_uris": { + "name": "redirect_uris", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "post_logout_redirect_uris": { + "name": "post_logout_redirect_uris", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "backchannel_logout_uri": { + "name": "backchannel_logout_uri", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "backchannel_logout_session_required": { + "name": "backchannel_logout_session_required", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "token_endpoint_auth_method": { + "name": "token_endpoint_auth_method", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "jwks": { + "name": "jwks", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "jwks_uri": { + "name": "jwks_uri", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "grant_types": { + "name": "grant_types", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "response_types": { + "name": "response_types", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "public": { + "name": "public", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "require_pkce": { + "name": "require_pkce", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dpop_bound_access_tokens": { + "name": "dpop_bound_access_tokens", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "oauthClient_client_id_unique": { + "name": "oauthClient_client_id_unique", + "columns": [ + "client_id" + ], + "isUnique": true + }, + "oauthClient_client_id_idx": { + "name": "oauthClient_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "oauthClient_user_id_idx": { + "name": "oauthClient_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauthClient_user_id_user_id_fk": { + "name": "oauthClient_user_id_user_id_fk", + "tableFrom": "oauthClient", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthClientAssertion": { + "name": "oauthClientAssertion", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthClientRegistration": { + "name": "oauthClientRegistration", + "columns": { + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "oauthClientRegistration_token_hash_unique": { + "name": "oauthClientRegistration_token_hash_unique", + "columns": [ + "token_hash" + ], + "isUnique": true + }, + "oauthClientRegistration_token_hash_idx": { + "name": "oauthClientRegistration_token_hash_idx", + "columns": [ + "token_hash" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauthClientRegistration_client_id_oauthClient_client_id_fk": { + "name": "oauthClientRegistration_client_id_oauthClient_client_id_fk", + "tableFrom": "oauthClientRegistration", + "tableTo": "oauthClient", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthClientResource": { + "name": "oauthClientResource", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "resource_id": { + "name": "resource_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "oauthClientResource_client_id_idx": { + "name": "oauthClientResource_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "oauthClientResource_resource_id_idx": { + "name": "oauthClientResource_resource_id_idx", + "columns": [ + "resource_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauthClientResource_client_id_oauthClient_client_id_fk": { + "name": "oauthClientResource_client_id_oauthClient_client_id_fk", + "tableFrom": "oauthClientResource", + "tableTo": "oauthClient", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauthClientResource_resource_id_oauthResource_identifier_fk": { + "name": "oauthClientResource_resource_id_oauthResource_identifier_fk", + "tableFrom": "oauthClientResource", + "tableTo": "oauthResource", + "columnsFrom": [ + "resource_id" + ], + "columnsTo": [ + "identifier" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthConsent": { + "name": "oauthConsent", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resources": { + "name": "resources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authorization_details": { + "name": "authorization_details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "last_used_at": { + "name": "last_used_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "oauthConsent_client_id_idx": { + "name": "oauthConsent_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "oauthConsent_user_id_idx": { + "name": "oauthConsent_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauthConsent_client_id_oauthClient_client_id_fk": { + "name": "oauthConsent_client_id_oauthClient_client_id_fk", + "tableFrom": "oauthConsent", + "tableTo": "oauthClient", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauthConsent_user_id_user_id_fk": { + "name": "oauthConsent_user_id_user_id_fk", + "tableFrom": "oauthConsent", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthJwtRevocation": { + "name": "oauthJwtRevocation", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "oauthJwtRevocation_expires_at_idx": { + "name": "oauthJwtRevocation_expires_at_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthPushedAuthorizationRequest": { + "name": "oauthPushedAuthorizationRequest", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "request_uri": { + "name": "request_uri", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "parameters": { + "name": "parameters", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "oauthPushedAuthorizationRequest_request_uri_unique": { + "name": "oauthPushedAuthorizationRequest_request_uri_unique", + "columns": [ + "request_uri" + ], + "isUnique": true + }, + "oauthPushedAuthorizationRequest_client_id_idx": { + "name": "oauthPushedAuthorizationRequest_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "oauthPushedAuthorizationRequest_expires_at_idx": { + "name": "oauthPushedAuthorizationRequest_expires_at_idx", + "columns": [ + "expires_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauthPushedAuthorizationRequest_client_id_oauthClient_client_id_fk": { + "name": "oauthPushedAuthorizationRequest_client_id_oauthClient_client_id_fk", + "tableFrom": "oauthPushedAuthorizationRequest", + "tableTo": "oauthClient", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthRefreshToken": { + "name": "oauthRefreshToken", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "client_id": { + "name": "client_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "session_id": { + "name": "session_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "reference_id": { + "name": "reference_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authorization_code_id": { + "name": "authorization_code_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "resources": { + "name": "resources", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "requested_user_info_claims": { + "name": "requested_user_info_claims", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "authorization_details": { + "name": "authorization_details", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "revoked": { + "name": "revoked", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rotated_at": { + "name": "rotated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rotation_replay_response": { + "name": "rotation_replay_response", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "rotation_replay_expires_at": { + "name": "rotation_replay_expires_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "auth_time": { + "name": "auth_time", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "confirmation": { + "name": "confirmation", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "scopes": { + "name": "scopes", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + } + }, + "indexes": { + "oauthRefreshToken_token_unique": { + "name": "oauthRefreshToken_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "oauthRefreshToken_client_id_idx": { + "name": "oauthRefreshToken_client_id_idx", + "columns": [ + "client_id" + ], + "isUnique": false + }, + "oauthRefreshToken_session_id_idx": { + "name": "oauthRefreshToken_session_id_idx", + "columns": [ + "session_id" + ], + "isUnique": false + }, + "oauthRefreshToken_user_id_idx": { + "name": "oauthRefreshToken_user_id_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "oauthRefreshToken_token_idx": { + "name": "oauthRefreshToken_token_idx", + "columns": [ + "token" + ], + "isUnique": false + } + }, + "foreignKeys": { + "oauthRefreshToken_client_id_oauthClient_client_id_fk": { + "name": "oauthRefreshToken_client_id_oauthClient_client_id_fk", + "tableFrom": "oauthRefreshToken", + "tableTo": "oauthClient", + "columnsFrom": [ + "client_id" + ], + "columnsTo": [ + "client_id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "oauthRefreshToken_session_id_session_id_fk": { + "name": "oauthRefreshToken_session_id_session_id_fk", + "tableFrom": "oauthRefreshToken", + "tableTo": "session", + "columnsFrom": [ + "session_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "oauthRefreshToken_user_id_user_id_fk": { + "name": "oauthRefreshToken_user_id_user_id_fk", + "tableFrom": "oauthRefreshToken", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "oauthResource": { + "name": "oauthResource", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "access_token_ttl": { + "name": "access_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "refresh_token_ttl": { + "name": "refresh_token_ttl", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signing_algorithm": { + "name": "signing_algorithm", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "signing_key_id": { + "name": "signing_key_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "allowed_scopes": { + "name": "allowed_scopes", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "custom_claims": { + "name": "custom_claims", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "dpop_bound_access_tokens_required": { + "name": "dpop_bound_access_tokens_required", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "disabled": { + "name": "disabled", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "policy_version": { + "name": "policy_version", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": 1 + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "oauthResource_identifier_unique": { + "name": "oauthResource_identifier_unique", + "columns": [ + "identifier" + ], + "isUnique": true + }, + "oauthResource_identifier_idx": { + "name": "oauthResource_identifier_idx", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "organization": { + "name": "organization", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "metadata": { + "name": "metadata", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "organization_slug_unique": { + "name": "organization_slug_unique", + "columns": [ + "slug" + ], + "isUnique": true + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "session": { + "name": "session", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "impersonated_by": { + "name": "impersonated_by", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "active_organization_id": { + "name": "active_organization_id", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + } + }, + "indexes": { + "session_token_unique": { + "name": "session_token_unique", + "columns": [ + "token" + ], + "isUnique": true + }, + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + "user_id" + ], + "isUnique": false + }, + "session_created_idx": { + "name": "session_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "user": { + "name": "user", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "email_verified": { + "name": "email_verified", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": false + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "banned": { + "name": "banned", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false, + "default": false + }, + "ban_reason": { + "name": "ban_reason", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "ban_expires": { + "name": "ban_expires", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "username": { + "name": "username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "display_username": { + "name": "display_username", + "type": "text", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "last_active_at": { + "name": "last_active_at", + "type": "integer", + "primaryKey": false, + "notNull": false, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "user_email_unique": { + "name": "user_email_unique", + "columns": [ + "email" + ], + "isUnique": true + }, + "user_username_unique": { + "name": "user_username_unique", + "columns": [ + "username" + ], + "isUnique": true + }, + "user_created_idx": { + "name": "user_created_idx", + "columns": [ + "created_at" + ], + "isUnique": false + }, + "user_lastActiveAt_idx": { + "name": "user_lastActiveAt_idx", + "columns": [ + "last_active_at" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + }, + "verification": { + "name": "verification", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "autoincrement": false + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "expires_at": { + "name": "expires_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false + }, + "created_at": { + "name": "created_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + }, + "updated_at": { + "name": "updated_at", + "type": "integer", + "primaryKey": false, + "notNull": true, + "autoincrement": false, + "default": "(cast(unixepoch('subsecond') * 1000 as integer))" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + "identifier" + ], + "isUnique": false + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "checkConstraints": {} + } + }, + "views": {}, + "enums": {}, + "_meta": { + "schemas": {}, + "tables": {}, + "columns": {} + }, + "internal": { + "indexes": { + "matters_webdav_children_idx": { + "columns": { + "\"dirtype\" desc": { + "isExpression": true + } + } + }, + "matters_list_page_idx": { + "columns": { + "\"dirtype\" desc": { + "isExpression": true + } + } + } + } + } +} \ No newline at end of file diff --git a/migrations/meta/_journal.json b/migrations/meta/_journal.json index 8ea79f34..5e369841 100644 --- a/migrations/meta/_journal.json +++ b/migrations/meta/_journal.json @@ -631,6 +631,13 @@ "when": 1785649535282, "tag": "0090_oauth-rar-par", "breakpoints": true + }, + { + "idx": 91, + "version": "6", + "when": 1785721689950, + "tag": "0091_oauth-client-registration-management", + "breakpoints": true } ] } \ No newline at end of file diff --git a/server/adapters/repos/oauth-client-registration.ts b/server/adapters/repos/oauth-client-registration.ts new file mode 100644 index 00000000..9ac69537 --- /dev/null +++ b/server/adapters/repos/oauth-client-registration.ts @@ -0,0 +1,71 @@ +import { and, eq } from 'drizzle-orm' +import { oauthClient, oauthClientRegistration, oauthClientResource, oauthResource } from '../../db/auth-schema' +import { executeWriteTransaction } from '../../db/transaction' +import type { Database } from '../../platform/interface' + +export type ManagedOAuthClient = typeof oauthClient.$inferSelect +export type ManagedOAuthClientUpdate = Partial + +export async function insertOAuthClientRegistration(db: Database, clientId: string, tokenHash: string): Promise { + await db.insert(oauthClientRegistration).values({ clientId, tokenHash }) +} + +export async function findManagedOAuthClient( + db: Database, + clientId: string, + tokenHash: string, +): Promise { + const [row] = await db + .select({ client: oauthClient }) + .from(oauthClientRegistration) + .innerJoin(oauthClient, eq(oauthClient.clientId, oauthClientRegistration.clientId)) + .where(and(eq(oauthClientRegistration.clientId, clientId), eq(oauthClientRegistration.tokenHash, tokenHash))) + .limit(1) + return row?.client ?? null +} + +export async function getManagedOAuthClient(db: Database, clientId: string): Promise { + const [client] = await db.select().from(oauthClient).where(eq(oauthClient.clientId, clientId)).limit(1) + return client ?? null +} + +export async function deleteManagedOAuthClient(db: Database, clientId: string): Promise { + await db.delete(oauthClient).where(eq(oauthClient.clientId, clientId)) +} + +export async function isOAuthResourceAvailable(db: Database, resourceId: string): Promise { + const [resource] = await db + .select({ disabled: oauthResource.disabled }) + .from(oauthResource) + .where(eq(oauthResource.identifier, resourceId)) + .limit(1) + return Boolean(resource && !resource.disabled) +} + +export async function replaceManagedOAuthClient( + db: Database, + clientId: string, + update: ManagedOAuthClientUpdate, + resourceIds: string[], +): Promise { + const resourceQueries = resourceIds.map((resourceId) => + db.insert(oauthClientResource).values({ + id: `${clientId}::${resourceId}`, + clientId, + resourceId, + }), + ) + await executeWriteTransaction(db, [ + db.update(oauthClient).set(update).where(eq(oauthClient.clientId, clientId)), + db.delete(oauthClientResource).where(eq(oauthClientResource.clientId, clientId)), + ...resourceQueries, + ]) +} + +export async function listManagedOAuthClientResources(db: Database, clientId: string): Promise { + const rows = await db + .select({ resourceId: oauthClientResource.resourceId }) + .from(oauthClientResource) + .where(eq(oauthClientResource.clientId, clientId)) + return rows.map((row) => row.resourceId) +} diff --git a/server/app.ts b/server/app.ts index 88fe9442..aaac9f34 100644 --- a/server/app.ts +++ b/server/app.ts @@ -6,6 +6,7 @@ import { OAUTH_RESOURCE_SCOPES, OAUTH_SCOPE_DESCRIPTIONS } from '@shared/oauth' import type { Context } from 'hono' import { cors } from 'hono/cors' import type { Auth } from './auth' +import { addOAuthClientRegistrationManagementOpenApi } from './auth/oauth-client-registration-management' import { cacheServerTiming, runWithCacheEvents } from './cache/context' import { createDeps } from './composition' import { isPotentialWebDavPublicRequest, isWebDavPublicRequest } from './domain/webdav-public-url' @@ -137,7 +138,7 @@ export function createApp(platform: Platform, auth: Auth, deps: Deps = createDep app.route('/api/auth/oauth2/authorization-details/catalog', oauthAuthorizationDetails) - app.on(['POST', 'GET', 'HEAD'], '/api/auth/*', async (c) => { + app.on(['POST', 'GET', 'HEAD', 'PUT', 'DELETE'], '/api/auth/*', async (c) => { const a = c.get('auth') const revokeRequest = c.req.path === '/api/auth/oauth2/revoke' ? c.req.raw.clone() : null const response = await a.handler(c.req.raw) @@ -281,6 +282,7 @@ export function createApp(platform: Platform, auth: Auth, deps: Deps = createDep }, }, } + addOAuthClientRegistrationManagementOpenApi(doc) doc.components.schemas = { ...(authDoc.components?.schemas as typeof doc.components.schemas), ...doc.components.schemas, diff --git a/server/auth.integration.test.ts b/server/auth.integration.test.ts index 0bd954a8..772758c7 100644 --- a/server/auth.integration.test.ts +++ b/server/auth.integration.test.ts @@ -874,9 +874,12 @@ describe('OAuth consent guards', () => { expect(body).toMatchObject({ client_id: expect.any(String), client_secret: expect.any(String), + registration_access_token: expect.stringMatching(/^zpr_/), + registration_client_uri: expect.stringMatching(/^http:\/\/localhost:3000\/api\/auth\/oauth2\/register\//), token_endpoint_auth_method: 'client_secret_basic', authorization_details_types: [WORKSPACE_AUTHORIZATION_DETAIL_TYPE], }) + expect(res.headers.get('Cache-Control')).toBe('no-store') expect(String(body.scope).split(' ')).toEqual( expect.arrayContaining(['openid', 'offline_access', 'workspaces:discover', 'objects:read']), ) @@ -898,6 +901,106 @@ describe('OAuth consent guards', () => { ) }) + it('reads, replaces, and deletes a dynamic client through its RFC 7592 configuration endpoint', async () => { + const ctx = await createTestApp() + const registration = await ctx.app.request('/api/auth/oauth2/register', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + client_name: 'Managed Broker', + redirect_uris: ['https://broker.example.com/oauth/callback'], + grant_types: ['authorization_code', 'refresh_token'], + response_types: ['code'], + token_endpoint_auth_method: 'client_secret_basic', + scope: 'openid offline_access', + }), + }) + const registered = (await registration.json()) as { + client_id: string + client_secret: string + registration_access_token: string + registration_client_uri: string + } + const authorization = { Authorization: `Bearer ${registered.registration_access_token}` } + const [storedManagementCredential] = await ctx.db.select().from(authSchema.oauthClientRegistration) + expect(storedManagementCredential).toMatchObject({ clientId: registered.client_id }) + expect(storedManagementCredential?.tokenHash).not.toBe(registered.registration_access_token) + + const unauthenticated = await ctx.app.request(registered.registration_client_uri) + expect(unauthenticated.status).toBe(401) + expect(unauthenticated.headers.get('WWW-Authenticate')).toContain('invalid_token') + + const read = await ctx.app.request(registered.registration_client_uri, { headers: authorization }) + expect(read.status).toBe(200) + const current = (await read.json()) as Record + expect(current).toMatchObject({ + client_id: registered.client_id, + client_name: 'Managed Broker', + registration_access_token: registered.registration_access_token, + registration_client_uri: registered.registration_client_uri, + }) + expect(current).not.toHaveProperty('client_secret') + + const update = await ctx.app.request(registered.registration_client_uri, { + method: 'PUT', + headers: { ...authorization, 'Content-Type': 'application/json' }, + body: JSON.stringify({ + client_id: registered.client_id, + client_secret: registered.client_secret, + client_name: 'Managed Broker v2', + redirect_uris: ['https://broker.example.com/oauth/callback-v2'], + grant_types: ['authorization_code', 'refresh_token'], + response_types: ['code'], + token_endpoint_auth_method: 'client_secret_basic', + scope: 'openid offline_access workspaces:discover', + authorization_details_types: [WORKSPACE_AUTHORIZATION_DETAIL_TYPE], + }), + }) + const updated = (await update.json()) as Record + expect(update.status, JSON.stringify(updated)).toBe(200) + expect(updated).toMatchObject({ + client_id: registered.client_id, + client_name: 'Managed Broker v2', + redirect_uris: ['https://broker.example.com/oauth/callback-v2'], + scope: 'openid offline_access workspaces:discover', + authorization_details_types: [WORKSPACE_AUTHORIZATION_DETAIL_TYPE], + }) + expect(updated).not.toHaveProperty('client_secret') + + const forbiddenServerMetadata = await ctx.app.request(registered.registration_client_uri, { + method: 'PUT', + headers: { ...authorization, 'Content-Type': 'application/json' }, + body: JSON.stringify({ client_id: registered.client_id, registration_access_token: 'replacement' }), + }) + expect(forbiddenServerMetadata.status).toBe(400) + await expect(forbiddenServerMetadata.json()).resolves.toMatchObject({ error: 'invalid_client_metadata' }) + + const wrongSecret = await ctx.app.request(registered.registration_client_uri, { + method: 'PUT', + headers: { ...authorization, 'Content-Type': 'application/json' }, + body: JSON.stringify({ + client_id: registered.client_id, + client_secret: 'not-the-issued-secret', + redirect_uris: ['https://broker.example.com/oauth/callback-v2'], + grant_types: ['authorization_code'], + response_types: ['code'], + token_endpoint_auth_method: 'client_secret_basic', + }), + }) + expect(wrongSecret.status).toBe(400) + + const deleted = await ctx.app.request(registered.registration_client_uri, { + method: 'DELETE', + headers: authorization, + }) + expect(deleted.status).toBe(204) + expect(deleted.headers.get('Cache-Control')).toBe('no-store') + + const readDeleted = await ctx.app.request(registered.registration_client_uri, { headers: authorization }) + expect(readDeleted.status).toBe(401) + expect(await ctx.db.select().from(authSchema.oauthClientRegistration)).toEqual([]) + }) + it('rejects unsupported authorization detail types during dynamic client registration', async () => { const ctx = await createTestApp() const response = await ctx.app.request('/api/auth/oauth2/register', { diff --git a/server/auth.ts b/server/auth.ts index 79d45182..2ee8032f 100644 --- a/server/auth.ts +++ b/server/auth.ts @@ -54,6 +54,7 @@ import { createSiteInvitationRepo } from './adapters/repos/site-invitations' import { initialStorageUsageProjectionQueries } from './adapters/repos/storage-usage-breakdown' import { createSystemOptionsRepo } from './adapters/repos/system-options' import { recordUserActivity } from './adapters/repos/user-activity' +import { handleOAuthClientRegistrationManagement } from './auth/oauth-client-registration-management' import { oauthPushedAuthorizationRequests } from './auth/oauth-par' import { createOAuthProviderOptions } from './auth/oauth-provider' import * as authSchema from './db/auth-schema' @@ -951,15 +952,17 @@ export async function createAuth( const defaultAuth = await createAuthInstance(false) let verificationAuth: typeof defaultAuth | null = null const dynamicHandler = async (request: Request): Promise => { - if (!usesEmailVerificationPolicy(request)) return defaultAuth.handler(request) + const handle = (auth: typeof defaultAuth) => + handleOAuthClientRegistrationManagement(request, db, (managedRequest) => auth.handler(managedRequest), baseURL) + if (!usesEmailVerificationPolicy(request)) return handle(defaultAuth) const required = isEmailVerificationRequired( await systemOptionsRepo.getValue(EMAIL_VERIFICATION_REQUIRED_OPTION_KEY), ) - if (!required) return defaultAuth.handler(request) + if (!required) return handle(defaultAuth) verificationAuth ??= await createAuthInstance(true) - return verificationAuth.handler(request) + return handle(verificationAuth) } return new Proxy(defaultAuth, { diff --git a/server/auth/oauth-client-registration-management.ts b/server/auth/oauth-client-registration-management.ts new file mode 100644 index 00000000..1f283d5d --- /dev/null +++ b/server/auth/oauth-client-registration-management.ts @@ -0,0 +1,474 @@ +import { z } from 'zod' +import { + JWT_BEARER_GRANT_TYPE, + OAUTH_SCOPES, + TOKEN_EXCHANGE_GRANT_TYPE, + WORKSPACE_AUTHORIZATION_DETAIL_TYPE, +} from '../../shared/oauth' +import { + deleteManagedOAuthClient, + findManagedOAuthClient, + getManagedOAuthClient, + insertOAuthClientRegistration, + isOAuthResourceAvailable, + listManagedOAuthClientResources, + type ManagedOAuthClient, + replaceManagedOAuthClient, +} from '../adapters/repos/oauth-client-registration' +import type { Database } from '../platform/interface' + +const NO_STORE_HEADERS = { + 'Cache-Control': 'no-store', + Pragma: 'no-cache', +} +const CLIENT_CONFIGURATION_PREFIX = '/api/auth/oauth2/register/' +const SUPPORTED_GRANTS = new Set([ + 'authorization_code', + 'refresh_token', + JWT_BEARER_GRANT_TYPE, + TOKEN_EXCHANGE_GRANT_TYPE, +]) +const FORBIDDEN_UPDATE_FIELDS = [ + 'registration_access_token', + 'registration_client_uri', + 'client_secret_expires_at', + 'client_id_issued_at', +] as const +const KNOWN_METADATA_FIELDS = new Set([ + 'client_id', + 'client_secret', + 'redirect_uris', + 'scope', + 'client_name', + 'client_uri', + 'logo_uri', + 'contacts', + 'tos_uri', + 'policy_uri', + 'software_id', + 'software_version', + 'software_statement', + 'post_logout_redirect_uris', + 'backchannel_logout_uri', + 'backchannel_logout_session_required', + 'token_endpoint_auth_method', + 'jwks', + 'jwks_uri', + 'grant_types', + 'response_types', + 'type', + 'subject_type', + 'dpop_bound_access_tokens', + 'resources', + 'require_pkce', +]) + +const absoluteUrl = z.string().url() +const updateSchema = z + .object({ + client_id: z.string().min(1), + client_secret: z.string().min(1).optional(), + redirect_uris: z.array(absoluteUrl).default([]), + scope: z.string().optional(), + client_name: z.string().optional(), + client_uri: absoluteUrl.optional(), + logo_uri: absoluteUrl.optional(), + contacts: z.array(z.string().min(1)).optional(), + tos_uri: absoluteUrl.optional(), + policy_uri: absoluteUrl.optional(), + software_id: z.string().optional(), + software_version: z.string().optional(), + software_statement: z.string().optional(), + post_logout_redirect_uris: z.array(absoluteUrl).optional(), + backchannel_logout_uri: absoluteUrl.optional(), + backchannel_logout_session_required: z.boolean().optional(), + token_endpoint_auth_method: z.string().min(1).default('client_secret_basic'), + jwks: z + .union([ + z.array(z.record(z.string(), z.unknown())), + z.object({ keys: z.array(z.record(z.string(), z.unknown())) }), + ]) + .optional(), + jwks_uri: absoluteUrl.optional(), + grant_types: z.array(z.string().min(1)).default(['authorization_code']), + response_types: z.array(z.literal('code')).optional(), + type: z.enum(['web', 'native', 'user-agent-based']).optional(), + subject_type: z.enum(['public', 'pairwise']).optional(), + dpop_bound_access_tokens: z.boolean().optional(), + authorization_details_types: z.array(z.string().min(1)).optional(), + resources: z.array(absoluteUrl).optional(), + require_pkce: z.boolean().optional(), + }) + .passthrough() + +export function addOAuthClientRegistrationManagementOpenApi(document: { paths: Record }): void { + const registration = document.paths['/api/auth/oauth2/register'] as + | { post?: { responses?: Record }> }> } } + | undefined + const registrationSchema = registration?.post?.responses?.['201']?.content?.['application/json']?.schema + if (registrationSchema) { + const properties = (registrationSchema.properties ?? {}) as Record + registrationSchema.properties = { + ...properties, + registration_client_uri: { type: 'string', format: 'uri' }, + registration_access_token: { type: 'string' }, + } + registrationSchema.required = [ + ...new Set([ + ...(Array.isArray(registrationSchema.required) ? registrationSchema.required : []), + 'registration_client_uri', + 'registration_access_token', + ]), + ] + } + + const clientInformationSchema = { + type: 'object', + additionalProperties: true, + properties: { + client_id: { type: 'string' }, + registration_client_uri: { type: 'string', format: 'uri' }, + registration_access_token: { type: 'string' }, + scope: { type: 'string' }, + }, + required: ['client_id', 'registration_client_uri', 'registration_access_token'], + } + const bearerSecurity = [{ bearerAuth: [] }] + document.paths['/api/auth/oauth2/register/{clientId}'] = { + parameters: [{ name: 'clientId', in: 'path', required: true, schema: { type: 'string' } }], + get: { + operationId: 'getDynamicOAuthClientRegistration', + summary: 'Read a dynamic OAuth client registration', + security: bearerSecurity, + responses: { + '200': { + description: 'Current client registration', + content: { 'application/json': { schema: clientInformationSchema } }, + }, + }, + }, + put: { + operationId: 'updateDynamicOAuthClientRegistration', + summary: 'Replace a dynamic OAuth client registration', + security: bearerSecurity, + requestBody: { + required: true, + content: { + 'application/json': { schema: { type: 'object', additionalProperties: true, required: ['client_id'] } }, + }, + }, + responses: { + '200': { + description: 'Updated client registration', + content: { 'application/json': { schema: clientInformationSchema } }, + }, + }, + }, + delete: { + operationId: 'deleteDynamicOAuthClientRegistration', + summary: 'Delete a dynamic OAuth client registration', + security: bearerSecurity, + responses: { '204': { description: 'Client registration deleted' } }, + }, + } +} + +export async function handleOAuthClientRegistrationManagement( + request: Request, + db: Database, + next: (request: Request) => Promise, + registrationBaseUrl?: string, +): Promise { + const url = new URL(request.url) + if (url.pathname === '/api/auth/oauth2/register' && request.method === 'POST') { + return augmentRegistrationResponse(request, db, next, registrationBaseUrl) + } + if (!url.pathname.startsWith(CLIENT_CONFIGURATION_PREFIX)) return next(request) + + const encodedClientId = url.pathname.slice(CLIENT_CONFIGURATION_PREFIX.length) + if (!encodedClientId || encodedClientId.includes('/')) return next(request) + let clientId: string + try { + clientId = decodeURIComponent(encodedClientId) + } catch { + return oauthJson(400, { error: 'invalid_request', error_description: 'Client identifier is malformed' }) + } + if (!['GET', 'PUT', 'DELETE'].includes(request.method)) { + return oauthJson( + 405, + { error: 'invalid_request', error_description: 'Method not allowed' }, + { Allow: 'GET, PUT, DELETE' }, + ) + } + + const authorization = request.headers.get('Authorization') + const token = bearerToken(authorization) + if (!token) { + return authorization + ? bearerError(400, 'invalid_request', 'The registration access token is malformed') + : bearerError(401, 'invalid_token', 'A registration access token is required') + } + const tokenHash = await hashToken(token) + const client = await findManagedOAuthClient(db, clientId, tokenHash) + if (!client) return bearerError(401, 'invalid_token', 'The registration access token is invalid') + const clientConfigurationUrl = configurationUrl(clientId, registrationBaseUrl ?? request.url) + + if (request.method === 'GET') + return oauthJson(200, await clientInformation(db, client, clientConfigurationUrl, token)) + if (request.method === 'DELETE') { + await deleteManagedOAuthClient(db, clientId) + return new Response(null, { status: 204, headers: NO_STORE_HEADERS }) + } + return updateClient(request, db, client, clientConfigurationUrl, token) +} + +async function augmentRegistrationResponse( + request: Request, + db: Database, + next: (request: Request) => Promise, + registrationBaseUrl?: string, +): Promise { + const response = await next(request) + if (response.status !== 201) return response + const body = (await response.clone().json()) as Record + const clientId = typeof body.client_id === 'string' ? body.client_id : null + if (!clientId) return response + + const token = registrationToken() + try { + await insertOAuthClientRegistration(db, clientId, await hashToken(token)) + } catch (error) { + await deleteManagedOAuthClient(db, clientId) + throw error + } + body.registration_client_uri = configurationUrl(clientId, registrationBaseUrl ?? request.url).href + body.registration_access_token = token + return Response.json(body, { + status: 201, + headers: mergedHeaders(response.headers, NO_STORE_HEADERS), + }) +} + +async function updateClient( + request: Request, + db: Database, + current: ManagedOAuthClient, + url: URL, + registrationToken: string, +): Promise { + if (!request.headers.get('Content-Type')?.toLowerCase().startsWith('application/json')) { + return oauthJson(415, { error: 'invalid_request', error_description: 'Content-Type must be application/json' }) + } + let input: unknown + try { + input = await request.json() + } catch { + return oauthJson(400, { error: 'invalid_request', error_description: 'Request body must be valid JSON' }) + } + const raw = input && typeof input === 'object' && !Array.isArray(input) ? (input as Record) : null + if (!raw) return invalidClientMetadata('Request body must be a JSON object') + const forbidden = FORBIDDEN_UPDATE_FIELDS.find((field) => field in raw) + if (forbidden) return invalidClientMetadata(`${forbidden} must not be included in an update request`) + + const parsed = updateSchema.safeParse(raw) + if (!parsed.success) return invalidClientMetadata(parsed.error.issues[0]?.message ?? 'Invalid client metadata') + const metadata = parsed.data + if (metadata.client_id !== current.clientId) + return invalidClientMetadata('client_id must match the registered client') + if (metadata.client_secret && !(await matchesStoredClientSecret(metadata.client_secret, current.clientSecret))) { + return invalidClientMetadata('client_secret must match the currently issued client secret') + } + if (metadata.token_endpoint_auth_method !== current.tokenEndpointAuthMethod) { + return invalidClientMetadata('token_endpoint_auth_method cannot be changed without rotating client credentials') + } + const validationError = validateMetadata(metadata) + if (validationError) return invalidClientMetadata(validationError) + for (const resourceId of metadata.resources ?? []) { + if (!(await isOAuthResourceAvailable(db, resourceId))) { + return oauthJson(400, { + error: 'invalid_target', + error_description: `requested resource ${resourceId} is unavailable`, + }) + } + } + + const extensionMetadata = Object.fromEntries( + Object.entries(raw).filter( + ([key]) => !KNOWN_METADATA_FIELDS.has(key) && !FORBIDDEN_UPDATE_FIELDS.includes(key as never), + ), + ) + await replaceManagedOAuthClient( + db, + current.clientId, + { + scopes: json(metadata.scope ? uniqueWords(metadata.scope) : undefined), + name: metadata.client_name ?? null, + uri: metadata.client_uri ?? null, + icon: metadata.logo_uri ?? null, + contacts: json(metadata.contacts), + tos: metadata.tos_uri ?? null, + policy: metadata.policy_uri ?? null, + softwareId: metadata.software_id ?? null, + softwareVersion: metadata.software_version ?? null, + softwareStatement: metadata.software_statement ?? null, + redirectUris: JSON.stringify(metadata.redirect_uris), + postLogoutRedirectUris: json(metadata.post_logout_redirect_uris), + backchannelLogoutUri: metadata.backchannel_logout_uri ?? null, + backchannelLogoutSessionRequired: metadata.backchannel_logout_session_required ?? null, + jwks: metadata.jwks ? JSON.stringify(normalizeJwks(metadata.jwks)) : null, + jwksUri: metadata.jwks_uri ?? null, + grantTypes: JSON.stringify(metadata.grant_types), + responseTypes: json(metadata.response_types), + type: metadata.type ?? null, + requirePKCE: metadata.require_pkce ?? null, + dpopBoundAccessTokens: metadata.dpop_bound_access_tokens ?? false, + subjectType: metadata.subject_type ?? null, + metadata: Object.keys(extensionMetadata).length > 0 ? JSON.stringify(extensionMetadata) : null, + updatedAt: new Date(), + }, + metadata.resources ?? [], + ) + + const updated = await getManagedOAuthClient(db, current.clientId) + if (!updated) return bearerError(401, 'invalid_token', 'The registered client no longer exists') + return oauthJson(200, await clientInformation(db, updated, url, registrationToken)) +} + +function validateMetadata(metadata: z.infer): string | null { + if (metadata.grant_types.some((grant) => !SUPPORTED_GRANTS.has(grant))) + return 'grant_types contains an unsupported grant type' + if (metadata.grant_types.includes('authorization_code')) { + if (metadata.redirect_uris.length === 0) return 'redirect_uris is required for authorization_code clients' + if (!metadata.response_types?.includes('code')) + return 'response_types must include code for authorization_code clients' + } else if (metadata.response_types?.includes('code')) { + return 'response_types cannot include code without the authorization_code grant' + } + if (uniqueWords(metadata.scope ?? '').some((scope) => !(OAUTH_SCOPES as readonly string[]).includes(scope))) { + return 'scope contains an unsupported scope' + } + if (metadata.authorization_details_types?.some((type) => type !== WORKSPACE_AUTHORIZATION_DETAIL_TYPE)) { + return 'authorization_details_types contains an unsupported type' + } + if (metadata.jwks && metadata.jwks_uri) return 'jwks and jwks_uri are mutually exclusive' + if (metadata.jwks_uri && new URL(metadata.jwks_uri).protocol !== 'https:') return 'jwks_uri must use HTTPS' + return null +} + +async function clientInformation( + db: Database, + client: ManagedOAuthClient, + requestUrl: URL, + registrationToken: string, +): Promise> { + const extensionMetadata = parseObject(client.metadata) + const resources = await listManagedOAuthClientResources(db, client.clientId) + return compact({ + ...extensionMetadata, + registration_access_token: registrationToken, + registration_client_uri: requestUrl.href, + client_id: client.clientId, + client_id_issued_at: Math.floor(client.createdAt.getTime() / 1000), + scope: parseArray(client.scopes)?.join(' '), + client_name: client.name, + client_uri: client.uri, + logo_uri: client.icon, + contacts: parseArray(client.contacts), + tos_uri: client.tos, + policy_uri: client.policy, + software_id: client.softwareId, + software_version: client.softwareVersion, + software_statement: client.softwareStatement, + redirect_uris: parseArray(client.redirectUris) ?? [], + post_logout_redirect_uris: parseArray(client.postLogoutRedirectUris), + backchannel_logout_uri: client.backchannelLogoutUri, + backchannel_logout_session_required: client.backchannelLogoutSessionRequired, + token_endpoint_auth_method: client.tokenEndpointAuthMethod, + jwks: client.jwks ? JSON.parse(client.jwks) : undefined, + jwks_uri: client.jwksUri, + grant_types: parseArray(client.grantTypes), + response_types: parseArray(client.responseTypes), + type: client.type, + require_pkce: client.requirePKCE, + dpop_bound_access_tokens: client.dpopBoundAccessTokens, + subject_type: client.subjectType, + resources: resources.length > 0 ? resources : undefined, + }) +} + +function bearerToken(header: string | null): string | null { + if (!header) return null + const match = /^Bearer ([A-Za-z0-9._~+/-]+=*)$/.exec(header) + return match?.[1] ?? null +} + +function bearerError(status: number, error: string, description: string): Response { + return oauthJson(status, { error, error_description: description }, { 'WWW-Authenticate': `Bearer error="${error}"` }) +} + +function invalidClientMetadata(description: string): Response { + return oauthJson(400, { error: 'invalid_client_metadata', error_description: description }) +} + +function oauthJson(status: number, body: Record, headers?: HeadersInit): Response { + return Response.json(body, { status, headers: mergedHeaders(headers, NO_STORE_HEADERS) }) +} + +function mergedHeaders(...inputs: Array): Headers { + const headers = new Headers() + for (const input of inputs) { + if (input) + new Headers(input).forEach((value, key) => { + headers.set(key, value) + }) + } + return headers +} + +function registrationToken(): string { + const bytes = crypto.getRandomValues(new Uint8Array(32)) + return `zpr_${base64Url(bytes)}` +} + +function configurationUrl(clientId: string, baseUrl: string): URL { + const endpoint = new URL(CLIENT_CONFIGURATION_PREFIX, baseUrl).href + return new URL(encodeURIComponent(clientId), endpoint) +} + +async function hashToken(token: string): Promise { + return base64Url(new Uint8Array(await crypto.subtle.digest('SHA-256', new TextEncoder().encode(token)))) +} + +async function matchesStoredClientSecret(candidate: string, stored: string | null): Promise { + return Boolean(stored) && (await hashToken(candidate)) === stored +} + +function base64Url(bytes: Uint8Array): string { + let binary = '' + for (const byte of bytes) binary += String.fromCharCode(byte) + return btoa(binary).replaceAll('+', '-').replaceAll('/', '_').replace(/=+$/, '') +} + +function uniqueWords(value: string): string[] { + return [...new Set(value.split(/\s+/).filter(Boolean))] +} + +function json(value: unknown[] | undefined): string | null { + return value === undefined ? null : JSON.stringify(value) +} + +function parseArray(value: string | null): string[] | undefined { + return value ? (JSON.parse(value) as string[]) : undefined +} + +function parseObject(value: string | null): Record { + return value ? (JSON.parse(value) as Record) : {} +} + +function normalizeJwks(value: Array> | { keys: Array> }) { + return Array.isArray(value) ? { keys: value } : value +} + +function compact(input: Record): Record { + return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined && value !== null)) +} diff --git a/server/db/auth-schema.test.ts b/server/db/auth-schema.test.ts index de874f0a..8a4e5b92 100644 --- a/server/db/auth-schema.test.ts +++ b/server/db/auth-schema.test.ts @@ -4,6 +4,7 @@ import { downloaderBootstrapCredential, oauthAccessToken, oauthClient, + oauthClientRegistration, oauthConsent, oauthPushedAuthorizationRequest, oauthRefreshToken, @@ -110,6 +111,17 @@ describe('OAuth tables', () => { ]) }) + it('stores hashed dynamic registration management credentials', () => { + const { foreignKeys, indexes } = getTableConfig(oauthClientRegistration) + + expect(oauthClientRegistration.clientId.name).toBe('client_id') + expect(oauthClientRegistration.tokenHash.name).toBe('token_hash') + expect(oauthClientRegistration.tokenHash.notNull).toBe(true) + expect(oauthClientRegistration.tokenHash.isUnique).toBe(true) + expect(foreignKeys[0]?.reference().foreignColumns[0].name).toBe('client_id') + expect(indexes.map((index) => index.config.name)).toEqual(['oauthClientRegistration_token_hash_idx']) + }) + it('declares refresh-token relationships and lookup indexes', () => { const { foreignKeys, indexes } = getTableConfig(oauthRefreshToken) diff --git a/server/db/auth-schema.ts b/server/db/auth-schema.ts index 605236ae..d3db22bc 100644 --- a/server/db/auth-schema.ts +++ b/server/db/auth-schema.ts @@ -280,6 +280,24 @@ export const oauthClient = sqliteTable( (table) => [index('oauthClient_client_id_idx').on(table.clientId), index('oauthClient_user_id_idx').on(table.userId)], ) +export const oauthClientRegistration = sqliteTable( + 'oauthClientRegistration', + { + clientId: text('client_id') + .primaryKey() + .references(() => oauthClient.clientId, { onDelete: 'cascade' }), + tokenHash: text('token_hash').notNull().unique(), + createdAt: integer('created_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull(), + }, + (table) => [index('oauthClientRegistration_token_hash_idx').on(table.tokenHash)], +) + export const oauthResource = sqliteTable( 'oauthResource', { diff --git a/server/openapi.test.ts b/server/openapi.test.ts index b09b5118..c0da287b 100644 --- a/server/openapi.test.ts +++ b/server/openapi.test.ts @@ -11,7 +11,7 @@ describe('global OpenAPI document', () => { expect(res.status).toBe(200) const doc = (await res.json()) as { openapi: string - paths: Record + paths: Record }> tags?: { name: string }[] } expect(doc.openapi).toBe('3.1.0') @@ -32,6 +32,7 @@ describe('global OpenAPI document', () => { '/api/downloads/downloaders/{id}', '/api/events', '/api/auth/oauth2/authorization-details/catalog', + '/api/auth/oauth2/register/{clientId}', '/api/objects', '/api/objects/{id}', '/api/objects/{id}/uploads/{uploadSessionId}/parts', @@ -46,6 +47,28 @@ describe('global OpenAPI document', () => { operationId: 'listAuthorizationDetailsCatalog', security: [{ oauth2: [AuthorizationScope.WORKSPACES_DISCOVER] }], }) + expect(doc.paths['/api/auth/oauth2/register/{clientId}']).toMatchObject({ + get: { operationId: 'getDynamicOAuthClientRegistration', security: [{ bearerAuth: [] }] }, + put: { operationId: 'updateDynamicOAuthClientRegistration', security: [{ bearerAuth: [] }] }, + delete: { operationId: 'deleteDynamicOAuthClientRegistration', security: [{ bearerAuth: [] }] }, + }) + expect(doc.paths['/api/auth/oauth2/register']?.post).toMatchObject({ + responses: { + '201': { + content: { + 'application/json': { + schema: { + properties: { + registration_client_uri: { type: 'string', format: 'uri' }, + registration_access_token: { type: 'string' }, + }, + required: expect.arrayContaining(['registration_client_uri', 'registration_access_token']), + }, + }, + }, + }, + }, + }) }) it('serves the Scalar reference UI at /api/docs pointing at the spec', async () => { diff --git a/server/test/setup.ts b/server/test/setup.ts index 4f50bdae..ea47618f 100644 --- a/server/test/setup.ts +++ b/server/test/setup.ts @@ -164,6 +164,13 @@ const AUTH_SCHEMA_SQL = ` ); CREATE INDEX IF NOT EXISTS oauthClient_client_id_idx ON oauthClient(client_id); CREATE INDEX IF NOT EXISTS oauthClient_user_id_idx ON oauthClient(user_id); + CREATE TABLE IF NOT EXISTS oauthClientRegistration ( + client_id TEXT PRIMARY KEY REFERENCES oauthClient(client_id) ON DELETE CASCADE, + token_hash TEXT NOT NULL UNIQUE, + created_at INTEGER NOT NULL DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)), + updated_at INTEGER NOT NULL DEFAULT (cast(unixepoch('subsecond') * 1000 as integer)) + ); + CREATE INDEX IF NOT EXISTS oauthClientRegistration_token_hash_idx ON oauthClientRegistration(token_hash); CREATE TABLE IF NOT EXISTS oauthResource ( id TEXT PRIMARY KEY, identifier TEXT NOT NULL UNIQUE,