mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add oauth2 token exchange (#12196)
Co-authored-by: Steven Masley <stevenmasley@gmail.com>
This commit is contained in:
Generated
+169
@@ -1491,6 +1491,146 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/oauth2/authorize": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Enterprise"
|
||||
],
|
||||
"summary": "OAuth2 authorization request.",
|
||||
"operationId": "oauth2-authorization-request",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client ID",
|
||||
"name": "client_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "A random unguessable string",
|
||||
"name": "state",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"code"
|
||||
],
|
||||
"type": "string",
|
||||
"description": "Response type",
|
||||
"name": "response_type",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Redirect here after authorization",
|
||||
"name": "redirect_uri",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Token scopes (currently ignored)",
|
||||
"name": "scope",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/oauth2/tokens": {
|
||||
"post": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Enterprise"
|
||||
],
|
||||
"summary": "OAuth2 token exchange.",
|
||||
"operationId": "oauth2-token-exchange",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client ID, required if grant_type=authorization_code",
|
||||
"name": "client_id",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client secret, required if grant_type=authorization_code",
|
||||
"name": "client_secret",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Authorization code, required if grant_type=authorization_code",
|
||||
"name": "code",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Refresh token, required if grant_type=refresh_token",
|
||||
"name": "refresh_token",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"enum": [
|
||||
"authorization_code",
|
||||
"refresh_token"
|
||||
],
|
||||
"type": "string",
|
||||
"description": "Grant type",
|
||||
"name": "grant_type",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/oauth2.Token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": [
|
||||
"Enterprise"
|
||||
],
|
||||
"summary": "Delete OAuth2 application tokens.",
|
||||
"operationId": "delete-oauth2-application-tokens",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client ID",
|
||||
"name": "client_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oauth2-provider/apps": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -1506,6 +1646,14 @@ const docTemplate = `{
|
||||
],
|
||||
"summary": "Get OAuth2 applications.",
|
||||
"operationId": "get-oauth2-applications",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by applications authorized for a user",
|
||||
"name": "user_id",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -13948,6 +14096,27 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"oauth2.Token": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"description": "AccessToken is the token that authorizes and authenticates\nthe requests.",
|
||||
"type": "string"
|
||||
},
|
||||
"expiry": {
|
||||
"description": "Expiry is the optional expiration time of the access token.\n\nIf zero, TokenSource implementations will reuse the same\ntoken forever and RefreshToken or equivalent\nmechanisms for that TokenSource will not be used.",
|
||||
"type": "string"
|
||||
},
|
||||
"refresh_token": {
|
||||
"description": "RefreshToken is a token that's used by the application\n(as opposed to the user) to refresh the access token\nif it expires.",
|
||||
"type": "string"
|
||||
},
|
||||
"token_type": {
|
||||
"description": "TokenType is the type of token.\nThe Type method returns either this or \"Bearer\", the default.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tailcfg.DERPHomeParams": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
Generated
+156
@@ -1291,6 +1291,133 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/oauth2/authorize": {
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "OAuth2 authorization request.",
|
||||
"operationId": "oauth2-authorization-request",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client ID",
|
||||
"name": "client_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "A random unguessable string",
|
||||
"name": "state",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"enum": ["code"],
|
||||
"type": "string",
|
||||
"description": "Response type",
|
||||
"name": "response_type",
|
||||
"in": "query",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Redirect here after authorization",
|
||||
"name": "redirect_uri",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Token scopes (currently ignored)",
|
||||
"name": "scope",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"302": {
|
||||
"description": "Found"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/login/oauth2/tokens": {
|
||||
"post": {
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "OAuth2 token exchange.",
|
||||
"operationId": "oauth2-token-exchange",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client ID, required if grant_type=authorization_code",
|
||||
"name": "client_id",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client secret, required if grant_type=authorization_code",
|
||||
"name": "client_secret",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Authorization code, required if grant_type=authorization_code",
|
||||
"name": "code",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Refresh token, required if grant_type=refresh_token",
|
||||
"name": "refresh_token",
|
||||
"in": "formData"
|
||||
},
|
||||
{
|
||||
"enum": ["authorization_code", "refresh_token"],
|
||||
"type": "string",
|
||||
"description": "Grant type",
|
||||
"name": "grant_type",
|
||||
"in": "formData",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/oauth2.Token"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "Delete OAuth2 application tokens.",
|
||||
"operationId": "delete-oauth2-application-tokens",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Client ID",
|
||||
"name": "client_id",
|
||||
"in": "query",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"description": "No Content"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/oauth2-provider/apps": {
|
||||
"get": {
|
||||
"security": [
|
||||
@@ -1302,6 +1429,14 @@
|
||||
"tags": ["Enterprise"],
|
||||
"summary": "Get OAuth2 applications.",
|
||||
"operationId": "get-oauth2-applications",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "Filter by applications authorized for a user",
|
||||
"name": "user_id",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
@@ -12716,6 +12851,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"oauth2.Token": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"access_token": {
|
||||
"description": "AccessToken is the token that authorizes and authenticates\nthe requests.",
|
||||
"type": "string"
|
||||
},
|
||||
"expiry": {
|
||||
"description": "Expiry is the optional expiration time of the access token.\n\nIf zero, TokenSource implementations will reuse the same\ntoken forever and RefreshToken or equivalent\nmechanisms for that TokenSource will not be used.",
|
||||
"type": "string"
|
||||
},
|
||||
"refresh_token": {
|
||||
"description": "RefreshToken is a token that's used by the application\n(as opposed to the user) to refresh the access token\nif it expires.",
|
||||
"type": "string"
|
||||
},
|
||||
"token_type": {
|
||||
"description": "TokenType is the type of token.\nThe Type method returns either this or \"Bearer\", the default.",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"tailcfg.DERPHomeParams": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package oidctest
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/dbauthz"
|
||||
@@ -114,3 +117,52 @@ func (h *LoginHelper) ForceRefresh(t *testing.T, db database.Store, user *coders
|
||||
_, err := user.User(testutil.Context(t, testutil.WaitShort), "me")
|
||||
require.NoError(t, err, "user must be able to be fetched")
|
||||
}
|
||||
|
||||
// OAuth2GetCode emulates a user clicking "allow" on the IDP page. When doing
|
||||
// unit tests, it's easier to skip this step sometimes. It does make an actual
|
||||
// request to the IDP, so it should be equivalent to doing this "manually" with
|
||||
// actual requests.
|
||||
func OAuth2GetCode(rawAuthURL string, doRequest func(req *http.Request) (*http.Response, error)) (string, error) {
|
||||
authURL, err := url.Parse(rawAuthURL)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("failed to parse auth URL: %w", err)
|
||||
}
|
||||
|
||||
r, err := http.NewRequestWithContext(context.Background(), http.MethodGet, rawAuthURL, nil)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("failed to create auth request: %w", err)
|
||||
}
|
||||
|
||||
expCode := http.StatusTemporaryRedirect
|
||||
resp, err := doRequest(r)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("request: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != expCode {
|
||||
return "", codersdk.ReadBodyAsError(resp)
|
||||
}
|
||||
|
||||
to := resp.Header.Get("Location")
|
||||
if to == "" {
|
||||
return "", xerrors.Errorf("expected redirect location")
|
||||
}
|
||||
|
||||
toURL, err := url.Parse(to)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("failed to parse redirect location: %w", err)
|
||||
}
|
||||
|
||||
code := toURL.Query().Get("code")
|
||||
if code == "" {
|
||||
return "", xerrors.Errorf("expected code in redirect location")
|
||||
}
|
||||
|
||||
state := authURL.Query().Get("state")
|
||||
newState := toURL.Query().Get("state")
|
||||
if newState != state {
|
||||
return "", xerrors.Errorf("expected state %q, got %q", state, newState)
|
||||
}
|
||||
return code, nil
|
||||
}
|
||||
|
||||
@@ -534,37 +534,18 @@ func (*FakeIDP) DeviceLogin(t testing.TB, client *codersdk.Client, externalAuthI
|
||||
// unit tests, it's easier to skip this step sometimes. It does make an actual
|
||||
// request to the IDP, so it should be equivalent to doing this "manually" with
|
||||
// actual requests.
|
||||
func (f *FakeIDP) CreateAuthCode(t testing.TB, state string, opts ...func(r *http.Request)) string {
|
||||
func (f *FakeIDP) CreateAuthCode(t testing.TB, state string) string {
|
||||
// We need to store some claims, because this is also an OIDC provider, and
|
||||
// it expects some claims to be present.
|
||||
f.stateToIDTokenClaims.Store(state, jwt.MapClaims{})
|
||||
|
||||
u := f.cfg.AuthCodeURL(state)
|
||||
r, err := http.NewRequestWithContext(context.Background(), http.MethodPost, u, nil)
|
||||
require.NoError(t, err, "failed to create auth request")
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(r)
|
||||
}
|
||||
|
||||
rw := httptest.NewRecorder()
|
||||
f.handler.ServeHTTP(rw, r)
|
||||
resp := rw.Result()
|
||||
defer resp.Body.Close()
|
||||
|
||||
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode, "expected redirect")
|
||||
to := resp.Header.Get("Location")
|
||||
require.NotEmpty(t, to, "expected redirect location")
|
||||
|
||||
toURL, err := url.Parse(to)
|
||||
require.NoError(t, err, "failed to parse redirect location")
|
||||
|
||||
code := toURL.Query().Get("code")
|
||||
require.NotEmpty(t, code, "expected code in redirect location")
|
||||
|
||||
newState := toURL.Query().Get("state")
|
||||
require.Equal(t, state, newState, "expected state to match")
|
||||
|
||||
code, err := OAuth2GetCode(f.cfg.AuthCodeURL(state), func(req *http.Request) (*http.Response, error) {
|
||||
rw := httptest.NewRecorder()
|
||||
f.handler.ServeHTTP(rw, req)
|
||||
resp := rw.Result()
|
||||
return resp, nil
|
||||
})
|
||||
require.NoError(t, err, "failed to get auth code")
|
||||
return code
|
||||
}
|
||||
|
||||
@@ -1071,7 +1052,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
|
||||
f.logger.Info(r.Context(), "http call device auth")
|
||||
|
||||
p := httpapi.NewQueryParamParser()
|
||||
p.Required("client_id")
|
||||
p.RequiredNotEmpty("client_id")
|
||||
clientID := p.String(r.URL.Query(), "", "client_id")
|
||||
_ = p.String(r.URL.Query(), "", "scopes")
|
||||
if len(p.Errors) > 0 {
|
||||
|
||||
@@ -801,6 +801,25 @@ func (q *querier) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID)
|
||||
return q.db.DeleteOAuth2ProviderAppByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error {
|
||||
code, err := q.db.GetOAuth2ProviderAppCodeByID(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := q.authorizeContext(ctx, rbac.ActionDelete, code); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.DeleteOAuth2ProviderAppCodeByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionDelete,
|
||||
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
|
||||
return err
|
||||
@@ -808,6 +827,14 @@ func (q *querier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid
|
||||
return q.db.DeleteOAuth2ProviderAppSecretByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionDelete,
|
||||
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) DeleteOldProvisionerDaemons(ctx context.Context) error {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionDelete, rbac.ResourceSystem); err != nil {
|
||||
return err
|
||||
@@ -1175,6 +1202,14 @@ func (q *querier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (d
|
||||
return q.db.GetOAuth2ProviderAppByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) {
|
||||
return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppCodeByID)(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (database.OAuth2ProviderAppCode, error) {
|
||||
return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppCodeByPrefix)(ctx, secretPrefix)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
|
||||
return database.OAuth2ProviderAppSecret{}, err
|
||||
@@ -1182,6 +1217,10 @@ func (q *querier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UU
|
||||
return q.db.GetOAuth2ProviderAppSecretByID(ctx, id)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppSecretByPrefix(ctx context.Context, secretPrefix []byte) (database.OAuth2ProviderAppSecret, error) {
|
||||
return fetch(q.log, q.auth, q.db.GetOAuth2ProviderAppSecretByPrefix)(ctx, secretPrefix)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID uuid.UUID) ([]database.OAuth2ProviderAppSecret, error) {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
|
||||
return []database.OAuth2ProviderAppSecret{}, err
|
||||
@@ -1189,6 +1228,22 @@ func (q *querier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID
|
||||
return q.db.GetOAuth2ProviderAppSecretsByAppID(ctx, appID)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppTokenByPrefix(ctx context.Context, hashPrefix []byte) (database.OAuth2ProviderAppToken, error) {
|
||||
token, err := q.db.GetOAuth2ProviderAppTokenByPrefix(ctx, hashPrefix)
|
||||
if err != nil {
|
||||
return database.OAuth2ProviderAppToken{}, err
|
||||
}
|
||||
// The user ID is on the API key so that has to be fetched.
|
||||
key, err := q.db.GetAPIKeyByID(ctx, token.APIKeyID)
|
||||
if err != nil {
|
||||
return database.OAuth2ProviderAppToken{}, err
|
||||
}
|
||||
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(key.UserID.String())); err != nil {
|
||||
return database.OAuth2ProviderAppToken{}, err
|
||||
}
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAuth2ProviderApp, error) {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionRead, rbac.ResourceOAuth2ProviderApp); err != nil {
|
||||
return []database.OAuth2ProviderApp{}, err
|
||||
@@ -1196,6 +1251,15 @@ func (q *querier) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAuth2P
|
||||
return q.db.GetOAuth2ProviderApps(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) {
|
||||
// This authz check is to make sure the caller can read all their own tokens.
|
||||
if err := q.authorizeContext(ctx, rbac.ActionRead,
|
||||
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(userID.String())); err != nil {
|
||||
return []database.GetOAuth2ProviderAppsByUserIDRow{}, err
|
||||
}
|
||||
return q.db.GetOAuth2ProviderAppsByUserID(ctx, userID)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuthSigningKey(ctx context.Context) (string, error) {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionUpdate, rbac.ResourceSystem); err != nil {
|
||||
return "", err
|
||||
@@ -2242,6 +2306,14 @@ func (q *querier) InsertOAuth2ProviderApp(ctx context.Context, arg database.Inse
|
||||
return q.db.InsertOAuth2ProviderApp(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertOAuth2ProviderAppCode(ctx context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionCreate,
|
||||
rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(arg.UserID.String())); err != nil {
|
||||
return database.OAuth2ProviderAppCode{}, err
|
||||
}
|
||||
return q.db.InsertOAuth2ProviderAppCode(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) {
|
||||
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceOAuth2ProviderAppSecret); err != nil {
|
||||
return database.OAuth2ProviderAppSecret{}, err
|
||||
@@ -2249,6 +2321,17 @@ func (q *querier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg databas
|
||||
return q.db.InsertOAuth2ProviderAppSecret(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertOAuth2ProviderAppToken(ctx context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) {
|
||||
key, err := q.db.GetAPIKeyByID(ctx, arg.APIKeyID)
|
||||
if err != nil {
|
||||
return database.OAuth2ProviderAppToken{}, err
|
||||
}
|
||||
if err := q.authorizeContext(ctx, rbac.ActionCreate, rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(key.UserID.String())); err != nil {
|
||||
return database.OAuth2ProviderAppToken{}, err
|
||||
}
|
||||
return q.db.InsertOAuth2ProviderAppToken(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) InsertOrganization(ctx context.Context, arg database.InsertOrganizationParams) (database.Organization, error) {
|
||||
return insert(q.log, q.auth, rbac.ResourceOrganization, q.db.InsertOrganization)(ctx, arg)
|
||||
}
|
||||
|
||||
@@ -2316,6 +2316,34 @@ func (s *MethodTestSuite) TestOAuth2ProviderApps() {
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
check.Args(app.ID).Asserts(rbac.ResourceOAuth2ProviderApp, rbac.ActionRead).Returns(app)
|
||||
}))
|
||||
s.Run("GetOAuth2ProviderAppsByUserID", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
|
||||
UserID: user.ID,
|
||||
})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
_ = dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
|
||||
AppID: app.ID,
|
||||
})
|
||||
for i := 0; i < 5; i++ {
|
||||
_ = dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{
|
||||
AppSecretID: secret.ID,
|
||||
APIKeyID: key.ID,
|
||||
})
|
||||
}
|
||||
check.Args(user.ID).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead).Returns([]database.GetOAuth2ProviderAppsByUserIDRow{
|
||||
{
|
||||
OAuth2ProviderApp: database.OAuth2ProviderApp{
|
||||
ID: app.ID,
|
||||
CallbackURL: app.CallbackURL,
|
||||
Icon: app.Icon,
|
||||
Name: app.Name,
|
||||
},
|
||||
TokenCount: 5,
|
||||
},
|
||||
})
|
||||
}))
|
||||
s.Run("InsertOAuth2ProviderApp", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(database.InsertOAuth2ProviderAppParams{}).Asserts(rbac.ResourceOAuth2ProviderApp, rbac.ActionCreate)
|
||||
}))
|
||||
@@ -2361,6 +2389,13 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppSecrets() {
|
||||
})
|
||||
check.Args(secret.ID).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionRead).Returns(secret)
|
||||
}))
|
||||
s.Run("GetOAuth2ProviderAppSecretByPrefix", s.Subtest(func(db database.Store, check *expects) {
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
|
||||
AppID: app.ID,
|
||||
})
|
||||
check.Args(secret.SecretPrefix).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionRead).Returns(secret)
|
||||
}))
|
||||
s.Run("InsertOAuth2ProviderAppSecret", s.Subtest(func(db database.Store, check *expects) {
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
check.Args(database.InsertOAuth2ProviderAppSecretParams{
|
||||
@@ -2386,3 +2421,107 @@ func (s *MethodTestSuite) TestOAuth2ProviderAppSecrets() {
|
||||
check.Args(secret.ID).Asserts(rbac.ResourceOAuth2ProviderAppSecret, rbac.ActionDelete)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestOAuth2ProviderAppCodes() {
|
||||
s.Run("GetOAuth2ProviderAppCodeByID", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
|
||||
AppID: app.ID,
|
||||
UserID: user.ID,
|
||||
})
|
||||
check.Args(code.ID).Asserts(code, rbac.ActionRead).Returns(code)
|
||||
}))
|
||||
s.Run("GetOAuth2ProviderAppCodeByPrefix", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
|
||||
AppID: app.ID,
|
||||
UserID: user.ID,
|
||||
})
|
||||
check.Args(code.SecretPrefix).Asserts(code, rbac.ActionRead).Returns(code)
|
||||
}))
|
||||
s.Run("InsertOAuth2ProviderAppCode", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
check.Args(database.InsertOAuth2ProviderAppCodeParams{
|
||||
AppID: app.ID,
|
||||
UserID: user.ID,
|
||||
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionCreate)
|
||||
}))
|
||||
s.Run("DeleteOAuth2ProviderAppCodeByID", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
code := dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
|
||||
AppID: app.ID,
|
||||
UserID: user.ID,
|
||||
})
|
||||
check.Args(code.ID).Asserts(code, rbac.ActionDelete)
|
||||
}))
|
||||
s.Run("DeleteOAuth2ProviderAppCodesByAppAndUserID", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
for i := 0; i < 5; i++ {
|
||||
_ = dbgen.OAuth2ProviderAppCode(s.T(), db, database.OAuth2ProviderAppCode{
|
||||
AppID: app.ID,
|
||||
UserID: user.ID,
|
||||
})
|
||||
}
|
||||
check.Args(database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams{
|
||||
AppID: app.ID,
|
||||
UserID: user.ID,
|
||||
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestOAuth2ProviderAppTokens() {
|
||||
s.Run("InsertOAuth2ProviderAppToken", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
|
||||
UserID: user.ID,
|
||||
})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
|
||||
AppID: app.ID,
|
||||
})
|
||||
check.Args(database.InsertOAuth2ProviderAppTokenParams{
|
||||
AppSecretID: secret.ID,
|
||||
APIKeyID: key.ID,
|
||||
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionCreate)
|
||||
}))
|
||||
s.Run("GetOAuth2ProviderAppTokenByPrefix", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
|
||||
UserID: user.ID,
|
||||
})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
|
||||
AppID: app.ID,
|
||||
})
|
||||
token := dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{
|
||||
AppSecretID: secret.ID,
|
||||
APIKeyID: key.ID,
|
||||
})
|
||||
check.Args(token.HashPrefix).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionRead)
|
||||
}))
|
||||
s.Run("DeleteOAuth2ProviderAppTokensByAppAndUserID", s.Subtest(func(db database.Store, check *expects) {
|
||||
user := dbgen.User(s.T(), db, database.User{})
|
||||
key, _ := dbgen.APIKey(s.T(), db, database.APIKey{
|
||||
UserID: user.ID,
|
||||
})
|
||||
app := dbgen.OAuth2ProviderApp(s.T(), db, database.OAuth2ProviderApp{})
|
||||
secret := dbgen.OAuth2ProviderAppSecret(s.T(), db, database.OAuth2ProviderAppSecret{
|
||||
AppID: app.ID,
|
||||
})
|
||||
for i := 0; i < 5; i++ {
|
||||
_ = dbgen.OAuth2ProviderAppToken(s.T(), db, database.OAuth2ProviderAppToken{
|
||||
AppSecretID: secret.ID,
|
||||
APIKeyID: key.ID,
|
||||
})
|
||||
}
|
||||
check.Args(database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams{
|
||||
AppID: app.ID,
|
||||
UserID: user.ID,
|
||||
}).Asserts(rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(user.ID.String()), rbac.ActionDelete)
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -707,6 +707,7 @@ func OAuth2ProviderAppSecret(t testing.TB, db database.Store, seed database.OAut
|
||||
app, err := db.InsertOAuth2ProviderAppSecret(genCtx, database.InsertOAuth2ProviderAppSecretParams{
|
||||
ID: takeFirst(seed.ID, uuid.New()),
|
||||
CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()),
|
||||
SecretPrefix: takeFirstSlice(seed.SecretPrefix, []byte("prefix")),
|
||||
HashedSecret: takeFirstSlice(seed.HashedSecret, []byte("hashed-secret")),
|
||||
DisplaySecret: takeFirst(seed.DisplaySecret, "secret"),
|
||||
AppID: takeFirst(seed.AppID, uuid.New()),
|
||||
@@ -715,6 +716,34 @@ func OAuth2ProviderAppSecret(t testing.TB, db database.Store, seed database.OAut
|
||||
return app
|
||||
}
|
||||
|
||||
func OAuth2ProviderAppCode(t testing.TB, db database.Store, seed database.OAuth2ProviderAppCode) database.OAuth2ProviderAppCode {
|
||||
code, err := db.InsertOAuth2ProviderAppCode(genCtx, database.InsertOAuth2ProviderAppCodeParams{
|
||||
ID: takeFirst(seed.ID, uuid.New()),
|
||||
CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()),
|
||||
ExpiresAt: takeFirst(seed.CreatedAt, dbtime.Now()),
|
||||
SecretPrefix: takeFirstSlice(seed.SecretPrefix, []byte("prefix")),
|
||||
HashedSecret: takeFirstSlice(seed.HashedSecret, []byte("hashed-secret")),
|
||||
AppID: takeFirst(seed.AppID, uuid.New()),
|
||||
UserID: takeFirst(seed.UserID, uuid.New()),
|
||||
})
|
||||
require.NoError(t, err, "insert oauth2 app code")
|
||||
return code
|
||||
}
|
||||
|
||||
func OAuth2ProviderAppToken(t testing.TB, db database.Store, seed database.OAuth2ProviderAppToken) database.OAuth2ProviderAppToken {
|
||||
token, err := db.InsertOAuth2ProviderAppToken(genCtx, database.InsertOAuth2ProviderAppTokenParams{
|
||||
ID: takeFirst(seed.ID, uuid.New()),
|
||||
CreatedAt: takeFirst(seed.CreatedAt, dbtime.Now()),
|
||||
ExpiresAt: takeFirst(seed.CreatedAt, dbtime.Now()),
|
||||
HashPrefix: takeFirstSlice(seed.HashPrefix, []byte("prefix")),
|
||||
RefreshHash: takeFirstSlice(seed.RefreshHash, []byte("hashed-secret")),
|
||||
AppSecretID: takeFirst(seed.AppSecretID, uuid.New()),
|
||||
APIKeyID: takeFirst(seed.APIKeyID, uuid.New().String()),
|
||||
})
|
||||
require.NoError(t, err, "insert oauth2 app token")
|
||||
return token
|
||||
}
|
||||
|
||||
func must[V any](v V, err error) V {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
||||
+271
-18
@@ -1,6 +1,7 @@
|
||||
package dbmem
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
@@ -133,6 +134,8 @@ type data struct {
|
||||
licenses []database.License
|
||||
oauth2ProviderApps []database.OAuth2ProviderApp
|
||||
oauth2ProviderAppSecrets []database.OAuth2ProviderAppSecret
|
||||
oauth2ProviderAppCodes []database.OAuth2ProviderAppCode
|
||||
oauth2ProviderAppTokens []database.OAuth2ProviderAppToken
|
||||
parameterSchemas []database.ParameterSchema
|
||||
provisionerDaemons []database.ProvisionerDaemon
|
||||
provisionerJobLogs []database.ProvisionerJobLog
|
||||
@@ -1165,19 +1168,72 @@ func (q *FakeQuerier) DeleteOAuth2ProviderAppByID(_ context.Context, id uuid.UUI
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for index, app := range q.oauth2ProviderApps {
|
||||
if app.ID == id {
|
||||
q.oauth2ProviderApps[index] = q.oauth2ProviderApps[len(q.oauth2ProviderApps)-1]
|
||||
q.oauth2ProviderApps = q.oauth2ProviderApps[:len(q.oauth2ProviderApps)-1]
|
||||
index := slices.IndexFunc(q.oauth2ProviderApps, func(app database.OAuth2ProviderApp) bool {
|
||||
return app.ID == id
|
||||
})
|
||||
|
||||
secrets := []database.OAuth2ProviderAppSecret{}
|
||||
for _, secret := range q.oauth2ProviderAppSecrets {
|
||||
if secret.AppID != id {
|
||||
secrets = append(secrets, secret)
|
||||
}
|
||||
}
|
||||
q.oauth2ProviderAppSecrets = secrets
|
||||
if index < 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
q.oauth2ProviderApps[index] = q.oauth2ProviderApps[len(q.oauth2ProviderApps)-1]
|
||||
q.oauth2ProviderApps = q.oauth2ProviderApps[:len(q.oauth2ProviderApps)-1]
|
||||
|
||||
// Cascade delete secrets associated with the deleted app.
|
||||
var deletedSecretIDs []uuid.UUID
|
||||
q.oauth2ProviderAppSecrets = slices.DeleteFunc(q.oauth2ProviderAppSecrets, func(secret database.OAuth2ProviderAppSecret) bool {
|
||||
matches := secret.AppID == id
|
||||
if matches {
|
||||
deletedSecretIDs = append(deletedSecretIDs, secret.ID)
|
||||
}
|
||||
return matches
|
||||
})
|
||||
|
||||
// Cascade delete tokens through the deleted secrets.
|
||||
var keyIDsToDelete []string
|
||||
q.oauth2ProviderAppTokens = slices.DeleteFunc(q.oauth2ProviderAppTokens, func(token database.OAuth2ProviderAppToken) bool {
|
||||
matches := slice.Contains(deletedSecretIDs, token.AppSecretID)
|
||||
if matches {
|
||||
keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID)
|
||||
}
|
||||
return matches
|
||||
})
|
||||
|
||||
// Cascade delete API keys linked to the deleted tokens.
|
||||
q.apiKeys = slices.DeleteFunc(q.apiKeys, func(key database.APIKey) bool {
|
||||
return slices.Contains(keyIDsToDelete, key.ID)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) DeleteOAuth2ProviderAppCodeByID(_ context.Context, id uuid.UUID) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for index, code := range q.oauth2ProviderAppCodes {
|
||||
if code.ID == id {
|
||||
q.oauth2ProviderAppCodes[index] = q.oauth2ProviderAppCodes[len(q.oauth2ProviderAppCodes)-1]
|
||||
q.oauth2ProviderAppCodes = q.oauth2ProviderAppCodes[:len(q.oauth2ProviderAppCodes)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) DeleteOAuth2ProviderAppCodesByAppAndUserID(_ context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for index, code := range q.oauth2ProviderAppCodes {
|
||||
if code.AppID == arg.AppID && code.UserID == arg.UserID {
|
||||
q.oauth2ProviderAppCodes[index] = q.oauth2ProviderAppCodes[len(q.oauth2ProviderAppCodes)-1]
|
||||
q.oauth2ProviderAppCodes = q.oauth2ProviderAppCodes[:len(q.oauth2ProviderAppCodes)-1]
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1188,14 +1244,68 @@ func (q *FakeQuerier) DeleteOAuth2ProviderAppSecretByID(_ context.Context, id uu
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for index, secret := range q.oauth2ProviderAppSecrets {
|
||||
if secret.ID == id {
|
||||
q.oauth2ProviderAppSecrets[index] = q.oauth2ProviderAppSecrets[len(q.oauth2ProviderAppSecrets)-1]
|
||||
q.oauth2ProviderAppSecrets = q.oauth2ProviderAppSecrets[:len(q.oauth2ProviderAppSecrets)-1]
|
||||
return nil
|
||||
}
|
||||
index := slices.IndexFunc(q.oauth2ProviderAppSecrets, func(secret database.OAuth2ProviderAppSecret) bool {
|
||||
return secret.ID == id
|
||||
})
|
||||
|
||||
if index < 0 {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
return sql.ErrNoRows
|
||||
|
||||
q.oauth2ProviderAppSecrets[index] = q.oauth2ProviderAppSecrets[len(q.oauth2ProviderAppSecrets)-1]
|
||||
q.oauth2ProviderAppSecrets = q.oauth2ProviderAppSecrets[:len(q.oauth2ProviderAppSecrets)-1]
|
||||
|
||||
// Cascade delete tokens created through the deleted secret.
|
||||
var keyIDsToDelete []string
|
||||
q.oauth2ProviderAppTokens = slices.DeleteFunc(q.oauth2ProviderAppTokens, func(token database.OAuth2ProviderAppToken) bool {
|
||||
matches := token.AppSecretID == id
|
||||
if matches {
|
||||
keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID)
|
||||
}
|
||||
return matches
|
||||
})
|
||||
|
||||
// Cascade delete API keys linked to the deleted tokens.
|
||||
q.apiKeys = slices.DeleteFunc(q.apiKeys, func(key database.APIKey) bool {
|
||||
return slices.Contains(keyIDsToDelete, key.ID)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) DeleteOAuth2ProviderAppTokensByAppAndUserID(_ context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
var keyIDsToDelete []string
|
||||
q.oauth2ProviderAppTokens = slices.DeleteFunc(q.oauth2ProviderAppTokens, func(token database.OAuth2ProviderAppToken) bool {
|
||||
// Join secrets and keys to see if the token matches.
|
||||
secretIdx := slices.IndexFunc(q.oauth2ProviderAppSecrets, func(secret database.OAuth2ProviderAppSecret) bool {
|
||||
return secret.ID == token.AppSecretID
|
||||
})
|
||||
keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool {
|
||||
return key.ID == token.APIKeyID
|
||||
})
|
||||
matches := secretIdx != -1 &&
|
||||
q.oauth2ProviderAppSecrets[secretIdx].AppID == arg.AppID &&
|
||||
keyIdx != -1 && q.apiKeys[keyIdx].UserID == arg.UserID
|
||||
if matches {
|
||||
keyIDsToDelete = append(keyIDsToDelete, token.APIKeyID)
|
||||
}
|
||||
return matches
|
||||
})
|
||||
|
||||
// Cascade delete API keys linked to the deleted tokens.
|
||||
q.apiKeys = slices.DeleteFunc(q.apiKeys, func(key database.APIKey) bool {
|
||||
return slices.Contains(keyIDsToDelete, key.ID)
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) DeleteOldProvisionerDaemons(_ context.Context) error {
|
||||
@@ -2138,6 +2248,30 @@ func (q *FakeQuerier) GetOAuth2ProviderAppByID(_ context.Context, id uuid.UUID)
|
||||
return database.OAuth2ProviderApp{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppCodeByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for _, code := range q.oauth2ProviderAppCodes {
|
||||
if code.ID == id {
|
||||
return code, nil
|
||||
}
|
||||
}
|
||||
return database.OAuth2ProviderAppCode{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppCodeByPrefix(_ context.Context, secretPrefix []byte) (database.OAuth2ProviderAppCode, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for _, code := range q.oauth2ProviderAppCodes {
|
||||
if bytes.Equal(code.SecretPrefix, secretPrefix) {
|
||||
return code, nil
|
||||
}
|
||||
}
|
||||
return database.OAuth2ProviderAppCode{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppSecretByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
@@ -2150,6 +2284,18 @@ func (q *FakeQuerier) GetOAuth2ProviderAppSecretByID(_ context.Context, id uuid.
|
||||
return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppSecretByPrefix(_ context.Context, secretPrefix []byte) (database.OAuth2ProviderAppSecret, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for _, secret := range q.oauth2ProviderAppSecrets {
|
||||
if bytes.Equal(secret.SecretPrefix, secretPrefix) {
|
||||
return secret, nil
|
||||
}
|
||||
}
|
||||
return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppSecretsByAppID(_ context.Context, appID uuid.UUID) ([]database.OAuth2ProviderAppSecret, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
@@ -2178,6 +2324,18 @@ func (q *FakeQuerier) GetOAuth2ProviderAppSecretsByAppID(_ context.Context, appI
|
||||
return []database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppTokenByPrefix(_ context.Context, hashPrefix []byte) (database.OAuth2ProviderAppToken, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for _, token := range q.oauth2ProviderAppTokens {
|
||||
if bytes.Equal(token.HashPrefix, hashPrefix) {
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
return database.OAuth2ProviderAppToken{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderApps(_ context.Context) ([]database.OAuth2ProviderApp, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
@@ -2188,6 +2346,42 @@ func (q *FakeQuerier) GetOAuth2ProviderApps(_ context.Context) ([]database.OAuth
|
||||
return q.oauth2ProviderApps, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppsByUserID(_ context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
rows := []database.GetOAuth2ProviderAppsByUserIDRow{}
|
||||
for _, app := range q.oauth2ProviderApps {
|
||||
tokens := []database.OAuth2ProviderAppToken{}
|
||||
for _, secret := range q.oauth2ProviderAppSecrets {
|
||||
if secret.AppID == app.ID {
|
||||
for _, token := range q.oauth2ProviderAppTokens {
|
||||
if token.AppSecretID == secret.ID {
|
||||
keyIdx := slices.IndexFunc(q.apiKeys, func(key database.APIKey) bool {
|
||||
return key.ID == token.APIKeyID
|
||||
})
|
||||
if keyIdx != -1 && q.apiKeys[keyIdx].UserID == userID {
|
||||
tokens = append(tokens, token)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(tokens) > 0 {
|
||||
rows = append(rows, database.GetOAuth2ProviderAppsByUserIDRow{
|
||||
OAuth2ProviderApp: database.OAuth2ProviderApp{
|
||||
CallbackURL: app.CallbackURL,
|
||||
ID: app.ID,
|
||||
Icon: app.Icon,
|
||||
Name: app.Name,
|
||||
},
|
||||
TokenCount: int64(len(tokens)),
|
||||
})
|
||||
}
|
||||
}
|
||||
return rows, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuthSigningKey(_ context.Context) (string, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -5240,6 +5434,34 @@ func (q *FakeQuerier) InsertOAuth2ProviderApp(_ context.Context, arg database.In
|
||||
return app, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertOAuth2ProviderAppCode(_ context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.OAuth2ProviderAppCode{}, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for _, app := range q.oauth2ProviderApps {
|
||||
if app.ID == arg.AppID {
|
||||
code := database.OAuth2ProviderAppCode{
|
||||
ID: arg.ID,
|
||||
CreatedAt: arg.CreatedAt,
|
||||
ExpiresAt: arg.ExpiresAt,
|
||||
SecretPrefix: arg.SecretPrefix,
|
||||
HashedSecret: arg.HashedSecret,
|
||||
UserID: arg.UserID,
|
||||
AppID: arg.AppID,
|
||||
}
|
||||
q.oauth2ProviderAppCodes = append(q.oauth2ProviderAppCodes, code)
|
||||
return code, nil
|
||||
}
|
||||
}
|
||||
|
||||
return database.OAuth2ProviderAppCode{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertOAuth2ProviderAppSecret(_ context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
@@ -5254,6 +5476,7 @@ func (q *FakeQuerier) InsertOAuth2ProviderAppSecret(_ context.Context, arg datab
|
||||
secret := database.OAuth2ProviderAppSecret{
|
||||
ID: arg.ID,
|
||||
CreatedAt: arg.CreatedAt,
|
||||
SecretPrefix: arg.SecretPrefix,
|
||||
HashedSecret: arg.HashedSecret,
|
||||
DisplaySecret: arg.DisplaySecret,
|
||||
AppID: arg.AppID,
|
||||
@@ -5266,6 +5489,35 @@ func (q *FakeQuerier) InsertOAuth2ProviderAppSecret(_ context.Context, arg datab
|
||||
return database.OAuth2ProviderAppSecret{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertOAuth2ProviderAppToken(_ context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return database.OAuth2ProviderAppToken{}, err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for _, secret := range q.oauth2ProviderAppSecrets {
|
||||
if secret.ID == arg.AppSecretID {
|
||||
//nolint:gosimple // Go wants database.OAuth2ProviderAppToken(arg), but we cannot be sure the structs will remain identical.
|
||||
token := database.OAuth2ProviderAppToken{
|
||||
ID: arg.ID,
|
||||
CreatedAt: arg.CreatedAt,
|
||||
ExpiresAt: arg.ExpiresAt,
|
||||
HashPrefix: arg.HashPrefix,
|
||||
RefreshHash: arg.RefreshHash,
|
||||
APIKeyID: arg.APIKeyID,
|
||||
AppSecretID: arg.AppSecretID,
|
||||
}
|
||||
q.oauth2ProviderAppTokens = append(q.oauth2ProviderAppTokens, token)
|
||||
return token, nil
|
||||
}
|
||||
}
|
||||
|
||||
return database.OAuth2ProviderAppToken{}, sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) InsertOrganization(_ context.Context, arg database.InsertOrganizationParams) (database.Organization, error) {
|
||||
if err := validateDatabaseType(arg); err != nil {
|
||||
return database.Organization{}, err
|
||||
@@ -6372,6 +6624,7 @@ func (q *FakeQuerier) UpdateOAuth2ProviderAppSecretByID(_ context.Context, arg d
|
||||
newSecret := database.OAuth2ProviderAppSecret{
|
||||
ID: arg.ID,
|
||||
CreatedAt: secret.CreatedAt,
|
||||
SecretPrefix: secret.SecretPrefix,
|
||||
HashedSecret: secret.HashedSecret,
|
||||
DisplaySecret: secret.DisplaySecret,
|
||||
AppID: secret.AppID,
|
||||
|
||||
@@ -225,6 +225,20 @@ func (m metricsStore) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.U
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.DeleteOAuth2ProviderAppCodeByID(ctx, id)
|
||||
m.queryLatencies.WithLabelValues("DeleteOAuth2ProviderAppCodeByID").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("DeleteOAuth2ProviderAppCodesByAppAndUserID").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.DeleteOAuth2ProviderAppSecretByID(ctx, id)
|
||||
@@ -232,6 +246,13 @@ func (m metricsStore) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("DeleteOAuth2ProviderAppTokensByAppAndUserID").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) DeleteOldProvisionerDaemons(ctx context.Context) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.DeleteOldProvisionerDaemons(ctx)
|
||||
@@ -615,6 +636,20 @@ func (m metricsStore) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppCode, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppCodeByID(ctx, id)
|
||||
m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppCodeByID").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (database.OAuth2ProviderAppCode, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppCodeByPrefix(ctx, secretPrefix)
|
||||
m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppCodeByPrefix").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderAppSecret, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppSecretByID(ctx, id)
|
||||
@@ -622,6 +657,13 @@ func (m metricsStore) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uui
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderAppSecretByPrefix(ctx context.Context, secretPrefix []byte) (database.OAuth2ProviderAppSecret, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppSecretByPrefix(ctx, secretPrefix)
|
||||
m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppSecretByPrefix").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID uuid.UUID) ([]database.OAuth2ProviderAppSecret, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppSecretsByAppID(ctx, appID)
|
||||
@@ -629,6 +671,13 @@ func (m metricsStore) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, ap
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderAppTokenByPrefix(ctx context.Context, hashPrefix []byte) (database.OAuth2ProviderAppToken, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppTokenByPrefix(ctx, hashPrefix)
|
||||
m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppTokenByPrefix").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAuth2ProviderApp, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderApps(ctx)
|
||||
@@ -636,6 +685,13 @@ func (m metricsStore) GetOAuth2ProviderApps(ctx context.Context) ([]database.OAu
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppsByUserID(ctx, userID)
|
||||
m.queryLatencies.WithLabelValues("GetOAuth2ProviderAppsByUserID").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) GetOAuthSigningKey(ctx context.Context) (string, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuthSigningKey(ctx)
|
||||
@@ -1425,6 +1481,13 @@ func (m metricsStore) InsertOAuth2ProviderApp(ctx context.Context, arg database.
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertOAuth2ProviderAppCode(ctx context.Context, arg database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertOAuth2ProviderAppCode(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertOAuth2ProviderAppCode").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertOAuth2ProviderAppSecret(ctx context.Context, arg database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertOAuth2ProviderAppSecret(ctx, arg)
|
||||
@@ -1432,6 +1495,13 @@ func (m metricsStore) InsertOAuth2ProviderAppSecret(ctx context.Context, arg dat
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertOAuth2ProviderAppToken(ctx context.Context, arg database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.InsertOAuth2ProviderAppToken(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("InsertOAuth2ProviderAppToken").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m metricsStore) InsertOrganization(ctx context.Context, arg database.InsertOrganizationParams) (database.Organization, error) {
|
||||
start := time.Now()
|
||||
organization, err := m.s.InsertOrganization(ctx, arg)
|
||||
|
||||
@@ -342,6 +342,34 @@ func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppByID(arg0, arg1 any) *go
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppByID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteOAuth2ProviderAppCodeByID mocks base method.
|
||||
func (m *MockStore) DeleteOAuth2ProviderAppCodeByID(arg0 context.Context, arg1 uuid.UUID) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteOAuth2ProviderAppCodeByID", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteOAuth2ProviderAppCodeByID indicates an expected call of DeleteOAuth2ProviderAppCodeByID.
|
||||
func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppCodeByID(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppCodeByID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppCodeByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteOAuth2ProviderAppCodesByAppAndUserID mocks base method.
|
||||
func (m *MockStore) DeleteOAuth2ProviderAppCodesByAppAndUserID(arg0 context.Context, arg1 database.DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteOAuth2ProviderAppCodesByAppAndUserID", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteOAuth2ProviderAppCodesByAppAndUserID indicates an expected call of DeleteOAuth2ProviderAppCodesByAppAndUserID.
|
||||
func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppCodesByAppAndUserID(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppCodesByAppAndUserID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppCodesByAppAndUserID), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteOAuth2ProviderAppSecretByID mocks base method.
|
||||
func (m *MockStore) DeleteOAuth2ProviderAppSecretByID(arg0 context.Context, arg1 uuid.UUID) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -356,6 +384,20 @@ func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppSecretByID(arg0, arg1 an
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppSecretByID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppSecretByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteOAuth2ProviderAppTokensByAppAndUserID mocks base method.
|
||||
func (m *MockStore) DeleteOAuth2ProviderAppTokensByAppAndUserID(arg0 context.Context, arg1 database.DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "DeleteOAuth2ProviderAppTokensByAppAndUserID", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// DeleteOAuth2ProviderAppTokensByAppAndUserID indicates an expected call of DeleteOAuth2ProviderAppTokensByAppAndUserID.
|
||||
func (mr *MockStoreMockRecorder) DeleteOAuth2ProviderAppTokensByAppAndUserID(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteOAuth2ProviderAppTokensByAppAndUserID", reflect.TypeOf((*MockStore)(nil).DeleteOAuth2ProviderAppTokensByAppAndUserID), arg0, arg1)
|
||||
}
|
||||
|
||||
// DeleteOldProvisionerDaemons mocks base method.
|
||||
func (m *MockStore) DeleteOldProvisionerDaemons(arg0 context.Context) error {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1219,6 +1261,36 @@ func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppByID(arg0, arg1 any) *gomoc
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppByID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppCodeByID mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppCodeByID(arg0 context.Context, arg1 uuid.UUID) (database.OAuth2ProviderAppCode, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOAuth2ProviderAppCodeByID", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.OAuth2ProviderAppCode)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppCodeByID indicates an expected call of GetOAuth2ProviderAppCodeByID.
|
||||
func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppCodeByID(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppCodeByID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppCodeByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppCodeByPrefix mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppCodeByPrefix(arg0 context.Context, arg1 []byte) (database.OAuth2ProviderAppCode, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOAuth2ProviderAppCodeByPrefix", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.OAuth2ProviderAppCode)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppCodeByPrefix indicates an expected call of GetOAuth2ProviderAppCodeByPrefix.
|
||||
func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppCodeByPrefix(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppCodeByPrefix", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppCodeByPrefix), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppSecretByID mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppSecretByID(arg0 context.Context, arg1 uuid.UUID) (database.OAuth2ProviderAppSecret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1234,6 +1306,21 @@ func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppSecretByID(arg0, arg1 any)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppSecretByID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppSecretByID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppSecretByPrefix mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppSecretByPrefix(arg0 context.Context, arg1 []byte) (database.OAuth2ProviderAppSecret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOAuth2ProviderAppSecretByPrefix", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.OAuth2ProviderAppSecret)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppSecretByPrefix indicates an expected call of GetOAuth2ProviderAppSecretByPrefix.
|
||||
func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppSecretByPrefix(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppSecretByPrefix", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppSecretByPrefix), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppSecretsByAppID mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppSecretsByAppID(arg0 context.Context, arg1 uuid.UUID) ([]database.OAuth2ProviderAppSecret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1249,6 +1336,21 @@ func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppSecretsByAppID(arg0, arg1 a
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppSecretsByAppID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppSecretsByAppID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppTokenByPrefix mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppTokenByPrefix(arg0 context.Context, arg1 []byte) (database.OAuth2ProviderAppToken, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOAuth2ProviderAppTokenByPrefix", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.OAuth2ProviderAppToken)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppTokenByPrefix indicates an expected call of GetOAuth2ProviderAppTokenByPrefix.
|
||||
func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppTokenByPrefix(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppTokenByPrefix", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppTokenByPrefix), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderApps mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderApps(arg0 context.Context) ([]database.OAuth2ProviderApp, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -1264,6 +1366,21 @@ func (mr *MockStoreMockRecorder) GetOAuth2ProviderApps(arg0 any) *gomock.Call {
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderApps", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderApps), arg0)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppsByUserID mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppsByUserID(arg0 context.Context, arg1 uuid.UUID) ([]database.GetOAuth2ProviderAppsByUserIDRow, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOAuth2ProviderAppsByUserID", arg0, arg1)
|
||||
ret0, _ := ret[0].([]database.GetOAuth2ProviderAppsByUserIDRow)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppsByUserID indicates an expected call of GetOAuth2ProviderAppsByUserID.
|
||||
func (mr *MockStoreMockRecorder) GetOAuth2ProviderAppsByUserID(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2ProviderAppsByUserID", reflect.TypeOf((*MockStore)(nil).GetOAuth2ProviderAppsByUserID), arg0, arg1)
|
||||
}
|
||||
|
||||
// GetOAuthSigningKey mocks base method.
|
||||
func (m *MockStore) GetOAuthSigningKey(arg0 context.Context) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -2999,6 +3116,21 @@ func (mr *MockStoreMockRecorder) InsertOAuth2ProviderApp(arg0, arg1 any) *gomock
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderApp", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderApp), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertOAuth2ProviderAppCode mocks base method.
|
||||
func (m *MockStore) InsertOAuth2ProviderAppCode(arg0 context.Context, arg1 database.InsertOAuth2ProviderAppCodeParams) (database.OAuth2ProviderAppCode, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertOAuth2ProviderAppCode", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.OAuth2ProviderAppCode)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertOAuth2ProviderAppCode indicates an expected call of InsertOAuth2ProviderAppCode.
|
||||
func (mr *MockStoreMockRecorder) InsertOAuth2ProviderAppCode(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderAppCode", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderAppCode), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertOAuth2ProviderAppSecret mocks base method.
|
||||
func (m *MockStore) InsertOAuth2ProviderAppSecret(arg0 context.Context, arg1 database.InsertOAuth2ProviderAppSecretParams) (database.OAuth2ProviderAppSecret, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@@ -3014,6 +3146,21 @@ func (mr *MockStoreMockRecorder) InsertOAuth2ProviderAppSecret(arg0, arg1 any) *
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderAppSecret", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderAppSecret), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertOAuth2ProviderAppToken mocks base method.
|
||||
func (m *MockStore) InsertOAuth2ProviderAppToken(arg0 context.Context, arg1 database.InsertOAuth2ProviderAppTokenParams) (database.OAuth2ProviderAppToken, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "InsertOAuth2ProviderAppToken", arg0, arg1)
|
||||
ret0, _ := ret[0].(database.OAuth2ProviderAppToken)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// InsertOAuth2ProviderAppToken indicates an expected call of InsertOAuth2ProviderAppToken.
|
||||
func (mr *MockStoreMockRecorder) InsertOAuth2ProviderAppToken(arg0, arg1 any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "InsertOAuth2ProviderAppToken", reflect.TypeOf((*MockStore)(nil).InsertOAuth2ProviderAppToken), arg0, arg1)
|
||||
}
|
||||
|
||||
// InsertOrganization mocks base method.
|
||||
func (m *MockStore) InsertOrganization(arg0 context.Context, arg1 database.InsertOrganizationParams) (database.Organization, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
||||
Generated
+67
-4
@@ -67,7 +67,8 @@ CREATE TYPE login_type AS ENUM (
|
||||
'github',
|
||||
'oidc',
|
||||
'token',
|
||||
'none'
|
||||
'none',
|
||||
'oauth2_provider_app'
|
||||
);
|
||||
|
||||
COMMENT ON TYPE login_type IS 'Specifies the method of authentication. "none" is a special case in which no authentication method is allowed.';
|
||||
@@ -187,6 +188,17 @@ CREATE TYPE workspace_transition AS ENUM (
|
||||
'delete'
|
||||
);
|
||||
|
||||
CREATE FUNCTION delete_deleted_oauth2_provider_app_token_api_key() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
BEGIN
|
||||
DELETE FROM api_keys
|
||||
WHERE id = OLD.api_key_id;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE FUNCTION delete_deleted_user_resources() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
@@ -490,17 +502,42 @@ CREATE SEQUENCE licenses_id_seq
|
||||
|
||||
ALTER SEQUENCE licenses_id_seq OWNED BY licenses.id;
|
||||
|
||||
CREATE TABLE oauth2_provider_app_codes (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
expires_at timestamp with time zone NOT NULL,
|
||||
secret_prefix bytea NOT NULL,
|
||||
hashed_secret bytea NOT NULL,
|
||||
user_id uuid NOT NULL,
|
||||
app_id uuid NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON TABLE oauth2_provider_app_codes IS 'Codes are meant to be exchanged for access tokens.';
|
||||
|
||||
CREATE TABLE oauth2_provider_app_secrets (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
last_used_at timestamp with time zone,
|
||||
hashed_secret bytea NOT NULL,
|
||||
display_secret text NOT NULL,
|
||||
app_id uuid NOT NULL
|
||||
app_id uuid NOT NULL,
|
||||
secret_prefix bytea NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN oauth2_provider_app_secrets.display_secret IS 'The tail end of the original secret so secrets can be differentiated.';
|
||||
|
||||
CREATE TABLE oauth2_provider_app_tokens (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
expires_at timestamp with time zone NOT NULL,
|
||||
hash_prefix bytea NOT NULL,
|
||||
refresh_hash bytea NOT NULL,
|
||||
app_secret_id uuid NOT NULL,
|
||||
api_key_id text NOT NULL
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN oauth2_provider_app_tokens.refresh_hash IS 'Refresh tokens provide a way to refresh an access token (API key). An expired API key can be refreshed if this token is not yet expired, meaning this expiry can outlive an API key.';
|
||||
|
||||
CREATE TABLE oauth2_provider_apps (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
@@ -1354,12 +1391,24 @@ ALTER TABLE ONLY licenses
|
||||
ALTER TABLE ONLY licenses
|
||||
ADD CONSTRAINT licenses_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_secrets
|
||||
ADD CONSTRAINT oauth2_provider_app_secrets_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret);
|
||||
ALTER TABLE ONLY oauth2_provider_app_codes
|
||||
ADD CONSTRAINT oauth2_provider_app_codes_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_codes
|
||||
ADD CONSTRAINT oauth2_provider_app_codes_secret_prefix_key UNIQUE (secret_prefix);
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_secrets
|
||||
ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_secrets
|
||||
ADD CONSTRAINT oauth2_provider_app_secrets_secret_prefix_key UNIQUE (secret_prefix);
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_tokens
|
||||
ADD CONSTRAINT oauth2_provider_app_tokens_hash_prefix_key UNIQUE (hash_prefix);
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_tokens
|
||||
ADD CONSTRAINT oauth2_provider_app_tokens_pkey PRIMARY KEY (id);
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_apps
|
||||
ADD CONSTRAINT oauth2_provider_apps_name_key UNIQUE (name);
|
||||
|
||||
@@ -1572,6 +1621,8 @@ CREATE TRIGGER tailnet_notify_peer_change AFTER INSERT OR DELETE OR UPDATE ON ta
|
||||
|
||||
CREATE TRIGGER tailnet_notify_tunnel_change AFTER INSERT OR DELETE OR UPDATE ON tailnet_tunnels FOR EACH ROW EXECUTE FUNCTION tailnet_notify_tunnel_change();
|
||||
|
||||
CREATE TRIGGER trigger_delete_oauth2_provider_app_token AFTER DELETE ON oauth2_provider_app_tokens FOR EACH ROW EXECUTE FUNCTION delete_deleted_oauth2_provider_app_token_api_key();
|
||||
|
||||
CREATE TRIGGER trigger_insert_apikeys BEFORE INSERT ON api_keys FOR EACH ROW EXECUTE FUNCTION insert_apikey_fail_if_user_deleted();
|
||||
|
||||
CREATE TRIGGER trigger_update_users AFTER INSERT OR UPDATE ON users FOR EACH ROW WHEN ((new.deleted = true)) EXECUTE FUNCTION delete_deleted_user_resources();
|
||||
@@ -1605,9 +1656,21 @@ ALTER TABLE ONLY jfrog_xray_scans
|
||||
ALTER TABLE ONLY jfrog_xray_scans
|
||||
ADD CONSTRAINT jfrog_xray_scans_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_codes
|
||||
ADD CONSTRAINT oauth2_provider_app_codes_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_codes
|
||||
ADD CONSTRAINT oauth2_provider_app_codes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_secrets
|
||||
ADD CONSTRAINT oauth2_provider_app_secrets_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_tokens
|
||||
ADD CONSTRAINT oauth2_provider_app_tokens_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_tokens
|
||||
ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_fkey FOREIGN KEY (app_secret_id) REFERENCES oauth2_provider_app_secrets(id) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE ONLY organization_members
|
||||
ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
|
||||
|
||||
@@ -15,7 +15,11 @@ const (
|
||||
ForeignKeyGroupsOrganizationID ForeignKeyConstraint = "groups_organization_id_fkey" // ALTER TABLE ONLY groups ADD CONSTRAINT groups_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
ForeignKeyJfrogXrayScansAgentID ForeignKeyConstraint = "jfrog_xray_scans_agent_id_fkey" // ALTER TABLE ONLY jfrog_xray_scans ADD CONSTRAINT jfrog_xray_scans_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
|
||||
ForeignKeyJfrogXrayScansWorkspaceID ForeignKeyConstraint = "jfrog_xray_scans_workspace_id_fkey" // ALTER TABLE ONLY jfrog_xray_scans ADD CONSTRAINT jfrog_xray_scans_workspace_id_fkey FOREIGN KEY (workspace_id) REFERENCES workspaces(id) ON DELETE CASCADE;
|
||||
ForeignKeyOauth2ProviderAppCodesAppID ForeignKeyConstraint = "oauth2_provider_app_codes_app_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE;
|
||||
ForeignKeyOauth2ProviderAppCodesUserID ForeignKeyConstraint = "oauth2_provider_app_codes_user_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
ForeignKeyOauth2ProviderAppSecretsAppID ForeignKeyConstraint = "oauth2_provider_app_secrets_app_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_fkey FOREIGN KEY (app_id) REFERENCES oauth2_provider_apps(id) ON DELETE CASCADE;
|
||||
ForeignKeyOauth2ProviderAppTokensAPIKeyID ForeignKeyConstraint = "oauth2_provider_app_tokens_api_key_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_api_key_id_fkey FOREIGN KEY (api_key_id) REFERENCES api_keys(id) ON DELETE CASCADE;
|
||||
ForeignKeyOauth2ProviderAppTokensAppSecretID ForeignKeyConstraint = "oauth2_provider_app_tokens_app_secret_id_fkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_app_secret_id_fkey FOREIGN KEY (app_secret_id) REFERENCES oauth2_provider_app_secrets(id) ON DELETE CASCADE;
|
||||
ForeignKeyOrganizationMembersOrganizationIDUUID ForeignKeyConstraint = "organization_members_organization_id_uuid_fkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_organization_id_uuid_fkey FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE;
|
||||
ForeignKeyOrganizationMembersUserIDUUID ForeignKeyConstraint = "organization_members_user_id_uuid_fkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_user_id_uuid_fkey FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
|
||||
ForeignKeyParameterSchemasJobID ForeignKeyConstraint = "parameter_schemas_job_id_fkey" // ALTER TABLE ONLY parameter_schemas ADD CONSTRAINT parameter_schemas_job_id_fkey FOREIGN KEY (job_id) REFERENCES provisioner_jobs(id) ON DELETE CASCADE;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
DROP TRIGGER IF EXISTS trigger_delete_oauth2_provider_app_token ON oauth2_provider_app_tokens;
|
||||
DROP FUNCTION IF EXISTS delete_deleted_oauth2_provider_app_token_api_key;
|
||||
|
||||
DROP TABLE oauth2_provider_app_tokens;
|
||||
DROP TABLE oauth2_provider_app_codes;
|
||||
|
||||
-- It is not possible to drop enum values from enum types, so the UP on
|
||||
-- login_type has "IF NOT EXISTS".
|
||||
|
||||
-- The constraints on the secret prefix (which is used as an id embedded in the
|
||||
-- secret) are dropped, but avoid completely reverting back to the previous
|
||||
-- behavior since that will render existing secrets unusable once upgraded
|
||||
-- again. OAuth2 is blocked outside of development mode in previous versions,
|
||||
-- so users will not be able to create broken secrets. This is really just to
|
||||
-- make sure tests keep working (say for a bisect).
|
||||
ALTER TABLE ONLY oauth2_provider_app_secrets
|
||||
DROP CONSTRAINT oauth2_provider_app_secrets_secret_prefix_key,
|
||||
ALTER COLUMN secret_prefix DROP NOT NULL;
|
||||
@@ -0,0 +1,65 @@
|
||||
CREATE TABLE oauth2_provider_app_codes (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
expires_at timestamp with time zone NOT NULL,
|
||||
secret_prefix bytea NOT NULL,
|
||||
hashed_secret bytea NOT NULL,
|
||||
user_id uuid NOT NULL REFERENCES users (id) ON DELETE CASCADE,
|
||||
app_id uuid NOT NULL REFERENCES oauth2_provider_apps (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE(secret_prefix)
|
||||
);
|
||||
|
||||
COMMENT ON TABLE oauth2_provider_app_codes IS 'Codes are meant to be exchanged for access tokens.';
|
||||
|
||||
CREATE TABLE oauth2_provider_app_tokens (
|
||||
id uuid NOT NULL,
|
||||
created_at timestamp with time zone NOT NULL,
|
||||
expires_at timestamp with time zone NOT NULL,
|
||||
hash_prefix bytea NOT NULL,
|
||||
refresh_hash bytea NOT NULL,
|
||||
app_secret_id uuid NOT NULL REFERENCES oauth2_provider_app_secrets (id) ON DELETE CASCADE,
|
||||
api_key_id text NOT NULL REFERENCES api_keys (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE(hash_prefix)
|
||||
);
|
||||
|
||||
COMMENT ON COLUMN oauth2_provider_app_tokens.refresh_hash IS 'Refresh tokens provide a way to refresh an access token (API key). An expired API key can be refreshed if this token is not yet expired, meaning this expiry can outlive an API key.';
|
||||
|
||||
-- When we delete a token, delete the API key associated with it.
|
||||
CREATE FUNCTION delete_deleted_oauth2_provider_app_token_api_key() RETURNS trigger
|
||||
LANGUAGE plpgsql
|
||||
AS $$
|
||||
DECLARE
|
||||
BEGIN
|
||||
DELETE FROM api_keys
|
||||
WHERE id = OLD.api_key_id;
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$;
|
||||
|
||||
CREATE TRIGGER trigger_delete_oauth2_provider_app_token
|
||||
AFTER DELETE ON oauth2_provider_app_tokens
|
||||
FOR EACH ROW
|
||||
EXECUTE PROCEDURE delete_deleted_oauth2_provider_app_token_api_key();
|
||||
|
||||
ALTER TYPE login_type ADD VALUE IF NOT EXISTS 'oauth2_provider_app';
|
||||
|
||||
-- Switch to an ID we will prefix to the raw secret that we give to the user
|
||||
-- (instead of matching on the entire secret as the ID, since they will be
|
||||
-- salted and we can no longer do that). OAuth2 is blocked outside of
|
||||
-- development mode so there should be no production secrets unless they
|
||||
-- previously upgraded, in which case they keep their original prefixes and will
|
||||
-- be fine. Add a random ID for the development mode case so the upgrade does
|
||||
-- not fail, at least.
|
||||
ALTER TABLE ONLY oauth2_provider_app_secrets
|
||||
ADD COLUMN IF NOT EXISTS secret_prefix bytea NULL;
|
||||
|
||||
UPDATE oauth2_provider_app_secrets
|
||||
SET secret_prefix = substr(md5(random()::text), 0, 10)::bytea
|
||||
WHERE secret_prefix IS NULL;
|
||||
|
||||
ALTER TABLE ONLY oauth2_provider_app_secrets
|
||||
ALTER COLUMN secret_prefix SET NOT NULL,
|
||||
ADD CONSTRAINT oauth2_provider_app_secrets_secret_prefix_key UNIQUE (secret_prefix),
|
||||
DROP CONSTRAINT IF EXISTS oauth2_provider_app_secrets_app_id_hashed_secret_key;
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
INSERT INTO oauth2_provider_app_codes
|
||||
(id, created_at, expires_at, secret_prefix, hashed_secret, user_id, app_id)
|
||||
VALUES (
|
||||
'c0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11',
|
||||
'2023-06-15 10:23:54+00',
|
||||
'2023-06-15 10:23:54+00',
|
||||
CAST('abcdefg' AS bytea),
|
||||
CAST('abcdefg' AS bytea),
|
||||
'0ed9befc-4911-4ccf-a8e2-559bf72daa94',
|
||||
'a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11'
|
||||
);
|
||||
|
||||
INSERT INTO oauth2_provider_app_tokens
|
||||
(id, created_at, expires_at, hash_prefix, refresh_hash, app_secret_id, api_key_id)
|
||||
VALUES (
|
||||
'd0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11',
|
||||
'2023-06-15 10:25:33+00',
|
||||
'2023-12-15 11:40:20+00',
|
||||
CAST('gfedcba' AS bytea),
|
||||
CAST('abcdefg' AS bytea),
|
||||
'b0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11',
|
||||
'peuLZhMXt4'
|
||||
);
|
||||
@@ -290,6 +290,22 @@ func (l License) RBACObject() rbac.Object {
|
||||
return rbac.ResourceLicense.WithIDString(strconv.FormatInt(int64(l.ID), 10))
|
||||
}
|
||||
|
||||
func (c OAuth2ProviderAppCode) RBACObject() rbac.Object {
|
||||
return rbac.ResourceOAuth2ProviderAppCodeToken.WithOwner(c.UserID.String())
|
||||
}
|
||||
|
||||
func (OAuth2ProviderAppSecret) RBACObject() rbac.Object {
|
||||
return rbac.ResourceOAuth2ProviderAppSecret
|
||||
}
|
||||
|
||||
func (OAuth2ProviderApp) RBACObject() rbac.Object {
|
||||
return rbac.ResourceOAuth2ProviderApp
|
||||
}
|
||||
|
||||
func (a GetOAuth2ProviderAppsByUserIDRow) RBACObject() rbac.Object {
|
||||
return a.OAuth2ProviderApp.RBACObject()
|
||||
}
|
||||
|
||||
type WorkspaceAgentConnectionStatus struct {
|
||||
Status WorkspaceAgentStatus `json:"status"`
|
||||
FirstConnectedAt *time.Time `json:"first_connected_at"`
|
||||
|
||||
@@ -593,11 +593,12 @@ func AllLogSourceValues() []LogSource {
|
||||
type LoginType string
|
||||
|
||||
const (
|
||||
LoginTypePassword LoginType = "password"
|
||||
LoginTypeGithub LoginType = "github"
|
||||
LoginTypeOIDC LoginType = "oidc"
|
||||
LoginTypeToken LoginType = "token"
|
||||
LoginTypeNone LoginType = "none"
|
||||
LoginTypePassword LoginType = "password"
|
||||
LoginTypeGithub LoginType = "github"
|
||||
LoginTypeOIDC LoginType = "oidc"
|
||||
LoginTypeToken LoginType = "token"
|
||||
LoginTypeNone LoginType = "none"
|
||||
LoginTypeOAuth2ProviderApp LoginType = "oauth2_provider_app"
|
||||
)
|
||||
|
||||
func (e *LoginType) Scan(src interface{}) error {
|
||||
@@ -641,7 +642,8 @@ func (e LoginType) Valid() bool {
|
||||
LoginTypeGithub,
|
||||
LoginTypeOIDC,
|
||||
LoginTypeToken,
|
||||
LoginTypeNone:
|
||||
LoginTypeNone,
|
||||
LoginTypeOAuth2ProviderApp:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -654,6 +656,7 @@ func AllLoginTypeValues() []LoginType {
|
||||
LoginTypeOIDC,
|
||||
LoginTypeToken,
|
||||
LoginTypeNone,
|
||||
LoginTypeOAuth2ProviderApp,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1807,6 +1810,17 @@ type OAuth2ProviderApp struct {
|
||||
CallbackURL string `db:"callback_url" json:"callback_url"`
|
||||
}
|
||||
|
||||
// Codes are meant to be exchanged for access tokens.
|
||||
type OAuth2ProviderAppCode struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
|
||||
SecretPrefix []byte `db:"secret_prefix" json:"secret_prefix"`
|
||||
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
}
|
||||
|
||||
type OAuth2ProviderAppSecret struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
@@ -1815,6 +1829,18 @@ type OAuth2ProviderAppSecret struct {
|
||||
// The tail end of the original secret so secrets can be differentiated.
|
||||
DisplaySecret string `db:"display_secret" json:"display_secret"`
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
SecretPrefix []byte `db:"secret_prefix" json:"secret_prefix"`
|
||||
}
|
||||
|
||||
type OAuth2ProviderAppToken struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
|
||||
HashPrefix []byte `db:"hash_prefix" json:"hash_prefix"`
|
||||
// Refresh tokens provide a way to refresh an access token (API key). An expired API key can be refreshed if this token is not yet expired, meaning this expiry can outlive an API key.
|
||||
RefreshHash []byte `db:"refresh_hash" json:"refresh_hash"`
|
||||
AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"`
|
||||
APIKeyID string `db:"api_key_id" json:"api_key_id"`
|
||||
}
|
||||
|
||||
type Organization struct {
|
||||
|
||||
@@ -60,7 +60,10 @@ type sqlcQuerier interface {
|
||||
DeleteGroupMemberFromGroup(ctx context.Context, arg DeleteGroupMemberFromGroupParams) error
|
||||
DeleteLicense(ctx context.Context, id int32) (int32, error)
|
||||
DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) error
|
||||
DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error
|
||||
DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error
|
||||
DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) error
|
||||
DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error
|
||||
// Delete provisioner daemons that have been created at least a week ago
|
||||
// and have not connected to coderd since a week.
|
||||
// A provisioner daemon with "zeroed" last_seen_at column indicates possible
|
||||
@@ -131,9 +134,14 @@ type sqlcQuerier interface {
|
||||
GetLicenses(ctx context.Context) ([]License, error)
|
||||
GetLogoURL(ctx context.Context) (string, error)
|
||||
GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderApp, error)
|
||||
GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error)
|
||||
GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppCode, error)
|
||||
GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppSecret, error)
|
||||
GetOAuth2ProviderAppSecretByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppSecret, error)
|
||||
GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID uuid.UUID) ([]OAuth2ProviderAppSecret, error)
|
||||
GetOAuth2ProviderAppTokenByPrefix(ctx context.Context, hashPrefix []byte) (OAuth2ProviderAppToken, error)
|
||||
GetOAuth2ProviderApps(ctx context.Context) ([]OAuth2ProviderApp, error)
|
||||
GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]GetOAuth2ProviderAppsByUserIDRow, error)
|
||||
GetOAuthSigningKey(ctx context.Context) (string, error)
|
||||
GetOrganizationByID(ctx context.Context, id uuid.UUID) (Organization, error)
|
||||
GetOrganizationByName(ctx context.Context, name string) (Organization, error)
|
||||
@@ -290,7 +298,9 @@ type sqlcQuerier interface {
|
||||
// If the name conflicts, do nothing.
|
||||
InsertMissingGroups(ctx context.Context, arg InsertMissingGroupsParams) ([]Group, error)
|
||||
InsertOAuth2ProviderApp(ctx context.Context, arg InsertOAuth2ProviderAppParams) (OAuth2ProviderApp, error)
|
||||
InsertOAuth2ProviderAppCode(ctx context.Context, arg InsertOAuth2ProviderAppCodeParams) (OAuth2ProviderAppCode, error)
|
||||
InsertOAuth2ProviderAppSecret(ctx context.Context, arg InsertOAuth2ProviderAppSecretParams) (OAuth2ProviderAppSecret, error)
|
||||
InsertOAuth2ProviderAppToken(ctx context.Context, arg InsertOAuth2ProviderAppTokenParams) (OAuth2ProviderAppToken, error)
|
||||
InsertOrganization(ctx context.Context, arg InsertOrganizationParams) (Organization, error)
|
||||
InsertOrganizationMember(ctx context.Context, arg InsertOrganizationMemberParams) (OrganizationMember, error)
|
||||
InsertProvisionerJob(ctx context.Context, arg InsertProvisionerJobParams) (ProvisionerJob, error)
|
||||
|
||||
@@ -2691,6 +2691,29 @@ func (q *sqlQuerier) DeleteOAuth2ProviderAppByID(ctx context.Context, id uuid.UU
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteOAuth2ProviderAppCodeByID = `-- name: DeleteOAuth2ProviderAppCodeByID :exec
|
||||
DELETE FROM oauth2_provider_app_codes WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) DeleteOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppCodeByID, id)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteOAuth2ProviderAppCodesByAppAndUserID = `-- name: DeleteOAuth2ProviderAppCodesByAppAndUserID :exec
|
||||
DELETE FROM oauth2_provider_app_codes WHERE app_id = $1 AND user_id = $2
|
||||
`
|
||||
|
||||
type DeleteOAuth2ProviderAppCodesByAppAndUserIDParams struct {
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) DeleteOAuth2ProviderAppCodesByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppCodesByAppAndUserIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppCodesByAppAndUserID, arg.AppID, arg.UserID)
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteOAuth2ProviderAppSecretByID = `-- name: DeleteOAuth2ProviderAppSecretByID :exec
|
||||
DELETE FROM oauth2_provider_app_secrets WHERE id = $1
|
||||
`
|
||||
@@ -2700,6 +2723,28 @@ func (q *sqlQuerier) DeleteOAuth2ProviderAppSecretByID(ctx context.Context, id u
|
||||
return err
|
||||
}
|
||||
|
||||
const deleteOAuth2ProviderAppTokensByAppAndUserID = `-- name: DeleteOAuth2ProviderAppTokensByAppAndUserID :exec
|
||||
DELETE FROM
|
||||
oauth2_provider_app_tokens
|
||||
USING
|
||||
oauth2_provider_app_secrets, api_keys
|
||||
WHERE
|
||||
oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id
|
||||
AND api_keys.id = oauth2_provider_app_tokens.api_key_id
|
||||
AND oauth2_provider_app_secrets.app_id = $1
|
||||
AND api_keys.user_id = $2
|
||||
`
|
||||
|
||||
type DeleteOAuth2ProviderAppTokensByAppAndUserIDParams struct {
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) DeleteOAuth2ProviderAppTokensByAppAndUserID(ctx context.Context, arg DeleteOAuth2ProviderAppTokensByAppAndUserIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, deleteOAuth2ProviderAppTokensByAppAndUserID, arg.AppID, arg.UserID)
|
||||
return err
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppByID = `-- name: GetOAuth2ProviderAppByID :one
|
||||
SELECT id, created_at, updated_at, name, icon, callback_url FROM oauth2_provider_apps WHERE id = $1
|
||||
`
|
||||
@@ -2718,8 +2763,46 @@ func (q *sqlQuerier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppCodeByID = `-- name: GetOAuth2ProviderAppCodeByID :one
|
||||
SELECT id, created_at, expires_at, secret_prefix, hashed_secret, user_id, app_id FROM oauth2_provider_app_codes WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error) {
|
||||
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppCodeByID, id)
|
||||
var i OAuth2ProviderAppCode
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.SecretPrefix,
|
||||
&i.HashedSecret,
|
||||
&i.UserID,
|
||||
&i.AppID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppCodeByPrefix = `-- name: GetOAuth2ProviderAppCodeByPrefix :one
|
||||
SELECT id, created_at, expires_at, secret_prefix, hashed_secret, user_id, app_id FROM oauth2_provider_app_codes WHERE secret_prefix = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppCode, error) {
|
||||
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppCodeByPrefix, secretPrefix)
|
||||
var i OAuth2ProviderAppCode
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.SecretPrefix,
|
||||
&i.HashedSecret,
|
||||
&i.UserID,
|
||||
&i.AppID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppSecretByID = `-- name: GetOAuth2ProviderAppSecretByID :one
|
||||
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id FROM oauth2_provider_app_secrets WHERE id = $1
|
||||
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix FROM oauth2_provider_app_secrets WHERE id = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppSecret, error) {
|
||||
@@ -2732,12 +2815,32 @@ func (q *sqlQuerier) GetOAuth2ProviderAppSecretByID(ctx context.Context, id uuid
|
||||
&i.HashedSecret,
|
||||
&i.DisplaySecret,
|
||||
&i.AppID,
|
||||
&i.SecretPrefix,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppSecretByPrefix = `-- name: GetOAuth2ProviderAppSecretByPrefix :one
|
||||
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix FROM oauth2_provider_app_secrets WHERE secret_prefix = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2ProviderAppSecretByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppSecret, error) {
|
||||
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppSecretByPrefix, secretPrefix)
|
||||
var i OAuth2ProviderAppSecret
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.LastUsedAt,
|
||||
&i.HashedSecret,
|
||||
&i.DisplaySecret,
|
||||
&i.AppID,
|
||||
&i.SecretPrefix,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppSecretsByAppID = `-- name: GetOAuth2ProviderAppSecretsByAppID :many
|
||||
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id FROM oauth2_provider_app_secrets WHERE app_id = $1 ORDER BY (created_at, id) ASC
|
||||
SELECT id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix FROM oauth2_provider_app_secrets WHERE app_id = $1 ORDER BY (created_at, id) ASC
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, appID uuid.UUID) ([]OAuth2ProviderAppSecret, error) {
|
||||
@@ -2756,6 +2859,7 @@ func (q *sqlQuerier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, app
|
||||
&i.HashedSecret,
|
||||
&i.DisplaySecret,
|
||||
&i.AppID,
|
||||
&i.SecretPrefix,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -2770,6 +2874,25 @@ func (q *sqlQuerier) GetOAuth2ProviderAppSecretsByAppID(ctx context.Context, app
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppTokenByPrefix = `-- name: GetOAuth2ProviderAppTokenByPrefix :one
|
||||
SELECT id, created_at, expires_at, hash_prefix, refresh_hash, app_secret_id, api_key_id FROM oauth2_provider_app_tokens WHERE hash_prefix = $1
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2ProviderAppTokenByPrefix(ctx context.Context, hashPrefix []byte) (OAuth2ProviderAppToken, error) {
|
||||
row := q.db.QueryRowContext(ctx, getOAuth2ProviderAppTokenByPrefix, hashPrefix)
|
||||
var i OAuth2ProviderAppToken
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.HashPrefix,
|
||||
&i.RefreshHash,
|
||||
&i.AppSecretID,
|
||||
&i.APIKeyID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getOAuth2ProviderApps = `-- name: GetOAuth2ProviderApps :many
|
||||
SELECT id, created_at, updated_at, name, icon, callback_url FROM oauth2_provider_apps ORDER BY (name, id) ASC
|
||||
`
|
||||
@@ -2804,6 +2927,59 @@ func (q *sqlQuerier) GetOAuth2ProviderApps(ctx context.Context) ([]OAuth2Provide
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getOAuth2ProviderAppsByUserID = `-- name: GetOAuth2ProviderAppsByUserID :many
|
||||
SELECT
|
||||
COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count,
|
||||
oauth2_provider_apps.id, oauth2_provider_apps.created_at, oauth2_provider_apps.updated_at, oauth2_provider_apps.name, oauth2_provider_apps.icon, oauth2_provider_apps.callback_url
|
||||
FROM oauth2_provider_app_tokens
|
||||
INNER JOIN oauth2_provider_app_secrets
|
||||
ON oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id
|
||||
INNER JOIN oauth2_provider_apps
|
||||
ON oauth2_provider_apps.id = oauth2_provider_app_secrets.app_id
|
||||
INNER JOIN api_keys
|
||||
ON api_keys.id = oauth2_provider_app_tokens.api_key_id
|
||||
WHERE
|
||||
api_keys.user_id = $1
|
||||
GROUP BY
|
||||
oauth2_provider_apps.id
|
||||
`
|
||||
|
||||
type GetOAuth2ProviderAppsByUserIDRow struct {
|
||||
TokenCount int64 `db:"token_count" json:"token_count"`
|
||||
OAuth2ProviderApp OAuth2ProviderApp `db:"oauth2_provider_app" json:"oauth2_provider_app"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2ProviderAppsByUserID(ctx context.Context, userID uuid.UUID) ([]GetOAuth2ProviderAppsByUserIDRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getOAuth2ProviderAppsByUserID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetOAuth2ProviderAppsByUserIDRow
|
||||
for rows.Next() {
|
||||
var i GetOAuth2ProviderAppsByUserIDRow
|
||||
if err := rows.Scan(
|
||||
&i.TokenCount,
|
||||
&i.OAuth2ProviderApp.ID,
|
||||
&i.OAuth2ProviderApp.CreatedAt,
|
||||
&i.OAuth2ProviderApp.UpdatedAt,
|
||||
&i.OAuth2ProviderApp.Name,
|
||||
&i.OAuth2ProviderApp.Icon,
|
||||
&i.OAuth2ProviderApp.CallbackURL,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, i)
|
||||
}
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const insertOAuth2ProviderApp = `-- name: InsertOAuth2ProviderApp :one
|
||||
INSERT INTO oauth2_provider_apps (
|
||||
id,
|
||||
@@ -2852,10 +3028,64 @@ func (q *sqlQuerier) InsertOAuth2ProviderApp(ctx context.Context, arg InsertOAut
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertOAuth2ProviderAppCode = `-- name: InsertOAuth2ProviderAppCode :one
|
||||
INSERT INTO oauth2_provider_app_codes (
|
||||
id,
|
||||
created_at,
|
||||
expires_at,
|
||||
secret_prefix,
|
||||
hashed_secret,
|
||||
app_id,
|
||||
user_id
|
||||
) VALUES(
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
) RETURNING id, created_at, expires_at, secret_prefix, hashed_secret, user_id, app_id
|
||||
`
|
||||
|
||||
type InsertOAuth2ProviderAppCodeParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
|
||||
SecretPrefix []byte `db:"secret_prefix" json:"secret_prefix"`
|
||||
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
UserID uuid.UUID `db:"user_id" json:"user_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertOAuth2ProviderAppCode(ctx context.Context, arg InsertOAuth2ProviderAppCodeParams) (OAuth2ProviderAppCode, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppCode,
|
||||
arg.ID,
|
||||
arg.CreatedAt,
|
||||
arg.ExpiresAt,
|
||||
arg.SecretPrefix,
|
||||
arg.HashedSecret,
|
||||
arg.AppID,
|
||||
arg.UserID,
|
||||
)
|
||||
var i OAuth2ProviderAppCode
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.SecretPrefix,
|
||||
&i.HashedSecret,
|
||||
&i.UserID,
|
||||
&i.AppID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertOAuth2ProviderAppSecret = `-- name: InsertOAuth2ProviderAppSecret :one
|
||||
INSERT INTO oauth2_provider_app_secrets (
|
||||
id,
|
||||
created_at,
|
||||
secret_prefix,
|
||||
hashed_secret,
|
||||
display_secret,
|
||||
app_id
|
||||
@@ -2864,13 +3094,15 @@ INSERT INTO oauth2_provider_app_secrets (
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
) RETURNING id, created_at, last_used_at, hashed_secret, display_secret, app_id
|
||||
$5,
|
||||
$6
|
||||
) RETURNING id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix
|
||||
`
|
||||
|
||||
type InsertOAuth2ProviderAppSecretParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
SecretPrefix []byte `db:"secret_prefix" json:"secret_prefix"`
|
||||
HashedSecret []byte `db:"hashed_secret" json:"hashed_secret"`
|
||||
DisplaySecret string `db:"display_secret" json:"display_secret"`
|
||||
AppID uuid.UUID `db:"app_id" json:"app_id"`
|
||||
@@ -2880,6 +3112,7 @@ func (q *sqlQuerier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg Inse
|
||||
row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppSecret,
|
||||
arg.ID,
|
||||
arg.CreatedAt,
|
||||
arg.SecretPrefix,
|
||||
arg.HashedSecret,
|
||||
arg.DisplaySecret,
|
||||
arg.AppID,
|
||||
@@ -2892,6 +3125,60 @@ func (q *sqlQuerier) InsertOAuth2ProviderAppSecret(ctx context.Context, arg Inse
|
||||
&i.HashedSecret,
|
||||
&i.DisplaySecret,
|
||||
&i.AppID,
|
||||
&i.SecretPrefix,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const insertOAuth2ProviderAppToken = `-- name: InsertOAuth2ProviderAppToken :one
|
||||
INSERT INTO oauth2_provider_app_tokens (
|
||||
id,
|
||||
created_at,
|
||||
expires_at,
|
||||
hash_prefix,
|
||||
refresh_hash,
|
||||
app_secret_id,
|
||||
api_key_id
|
||||
) VALUES(
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
) RETURNING id, created_at, expires_at, hash_prefix, refresh_hash, app_secret_id, api_key_id
|
||||
`
|
||||
|
||||
type InsertOAuth2ProviderAppTokenParams struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
|
||||
HashPrefix []byte `db:"hash_prefix" json:"hash_prefix"`
|
||||
RefreshHash []byte `db:"refresh_hash" json:"refresh_hash"`
|
||||
AppSecretID uuid.UUID `db:"app_secret_id" json:"app_secret_id"`
|
||||
APIKeyID string `db:"api_key_id" json:"api_key_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) InsertOAuth2ProviderAppToken(ctx context.Context, arg InsertOAuth2ProviderAppTokenParams) (OAuth2ProviderAppToken, error) {
|
||||
row := q.db.QueryRowContext(ctx, insertOAuth2ProviderAppToken,
|
||||
arg.ID,
|
||||
arg.CreatedAt,
|
||||
arg.ExpiresAt,
|
||||
arg.HashPrefix,
|
||||
arg.RefreshHash,
|
||||
arg.AppSecretID,
|
||||
arg.APIKeyID,
|
||||
)
|
||||
var i OAuth2ProviderAppToken
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.ExpiresAt,
|
||||
&i.HashPrefix,
|
||||
&i.RefreshHash,
|
||||
&i.AppSecretID,
|
||||
&i.APIKeyID,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
@@ -2936,7 +3223,7 @@ func (q *sqlQuerier) UpdateOAuth2ProviderAppByID(ctx context.Context, arg Update
|
||||
const updateOAuth2ProviderAppSecretByID = `-- name: UpdateOAuth2ProviderAppSecretByID :one
|
||||
UPDATE oauth2_provider_app_secrets SET
|
||||
last_used_at = $2
|
||||
WHERE id = $1 RETURNING id, created_at, last_used_at, hashed_secret, display_secret, app_id
|
||||
WHERE id = $1 RETURNING id, created_at, last_used_at, hashed_secret, display_secret, app_id, secret_prefix
|
||||
`
|
||||
|
||||
type UpdateOAuth2ProviderAppSecretByIDParams struct {
|
||||
@@ -2954,6 +3241,7 @@ func (q *sqlQuerier) UpdateOAuth2ProviderAppSecretByID(ctx context.Context, arg
|
||||
&i.HashedSecret,
|
||||
&i.DisplaySecret,
|
||||
&i.AppID,
|
||||
&i.SecretPrefix,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
@@ -38,10 +38,14 @@ SELECT * FROM oauth2_provider_app_secrets WHERE id = $1;
|
||||
-- name: GetOAuth2ProviderAppSecretsByAppID :many
|
||||
SELECT * FROM oauth2_provider_app_secrets WHERE app_id = $1 ORDER BY (created_at, id) ASC;
|
||||
|
||||
-- name: GetOAuth2ProviderAppSecretByPrefix :one
|
||||
SELECT * FROM oauth2_provider_app_secrets WHERE secret_prefix = $1;
|
||||
|
||||
-- name: InsertOAuth2ProviderAppSecret :one
|
||||
INSERT INTO oauth2_provider_app_secrets (
|
||||
id,
|
||||
created_at,
|
||||
secret_prefix,
|
||||
hashed_secret,
|
||||
display_secret,
|
||||
app_id
|
||||
@@ -50,7 +54,8 @@ INSERT INTO oauth2_provider_app_secrets (
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5
|
||||
$5,
|
||||
$6
|
||||
) RETURNING *;
|
||||
|
||||
-- name: UpdateOAuth2ProviderAppSecretByID :one
|
||||
@@ -60,3 +65,83 @@ WHERE id = $1 RETURNING *;
|
||||
|
||||
-- name: DeleteOAuth2ProviderAppSecretByID :exec
|
||||
DELETE FROM oauth2_provider_app_secrets WHERE id = $1;
|
||||
|
||||
-- name: GetOAuth2ProviderAppCodeByID :one
|
||||
SELECT * FROM oauth2_provider_app_codes WHERE id = $1;
|
||||
|
||||
-- name: GetOAuth2ProviderAppCodeByPrefix :one
|
||||
SELECT * FROM oauth2_provider_app_codes WHERE secret_prefix = $1;
|
||||
|
||||
-- name: InsertOAuth2ProviderAppCode :one
|
||||
INSERT INTO oauth2_provider_app_codes (
|
||||
id,
|
||||
created_at,
|
||||
expires_at,
|
||||
secret_prefix,
|
||||
hashed_secret,
|
||||
app_id,
|
||||
user_id
|
||||
) VALUES(
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
) RETURNING *;
|
||||
|
||||
-- name: DeleteOAuth2ProviderAppCodeByID :exec
|
||||
DELETE FROM oauth2_provider_app_codes WHERE id = $1;
|
||||
|
||||
-- name: DeleteOAuth2ProviderAppCodesByAppAndUserID :exec
|
||||
DELETE FROM oauth2_provider_app_codes WHERE app_id = $1 AND user_id = $2;
|
||||
|
||||
-- name: InsertOAuth2ProviderAppToken :one
|
||||
INSERT INTO oauth2_provider_app_tokens (
|
||||
id,
|
||||
created_at,
|
||||
expires_at,
|
||||
hash_prefix,
|
||||
refresh_hash,
|
||||
app_secret_id,
|
||||
api_key_id
|
||||
) VALUES(
|
||||
$1,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
$7
|
||||
) RETURNING *;
|
||||
|
||||
-- name: GetOAuth2ProviderAppTokenByPrefix :one
|
||||
SELECT * FROM oauth2_provider_app_tokens WHERE hash_prefix = $1;
|
||||
|
||||
-- name: GetOAuth2ProviderAppsByUserID :many
|
||||
SELECT
|
||||
COUNT(DISTINCT oauth2_provider_app_tokens.id) as token_count,
|
||||
sqlc.embed(oauth2_provider_apps)
|
||||
FROM oauth2_provider_app_tokens
|
||||
INNER JOIN oauth2_provider_app_secrets
|
||||
ON oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id
|
||||
INNER JOIN oauth2_provider_apps
|
||||
ON oauth2_provider_apps.id = oauth2_provider_app_secrets.app_id
|
||||
INNER JOIN api_keys
|
||||
ON api_keys.id = oauth2_provider_app_tokens.api_key_id
|
||||
WHERE
|
||||
api_keys.user_id = $1
|
||||
GROUP BY
|
||||
oauth2_provider_apps.id;
|
||||
|
||||
-- name: DeleteOAuth2ProviderAppTokensByAppAndUserID :exec
|
||||
DELETE FROM
|
||||
oauth2_provider_app_tokens
|
||||
USING
|
||||
oauth2_provider_app_secrets, api_keys
|
||||
WHERE
|
||||
oauth2_provider_app_secrets.id = oauth2_provider_app_tokens.app_secret_id
|
||||
AND api_keys.id = oauth2_provider_app_tokens.api_key_id
|
||||
AND oauth2_provider_app_secrets.app_id = $1
|
||||
AND api_keys.user_id = $2;
|
||||
|
||||
@@ -99,4 +99,8 @@ sql:
|
||||
display_app_ssh_helper: DisplayAppSSHHelper
|
||||
oauth2_provider_app: OAuth2ProviderApp
|
||||
oauth2_provider_app_secret: OAuth2ProviderAppSecret
|
||||
oauth2_provider_app_code: OAuth2ProviderAppCode
|
||||
oauth2_provider_app_token: OAuth2ProviderAppToken
|
||||
api_key_id: APIKeyID
|
||||
callback_url: CallbackURL
|
||||
login_type_oauth2_provider_app: LoginTypeOAuth2ProviderApp
|
||||
|
||||
@@ -22,8 +22,12 @@ const (
|
||||
UniqueJfrogXrayScansPkey UniqueConstraint = "jfrog_xray_scans_pkey" // ALTER TABLE ONLY jfrog_xray_scans ADD CONSTRAINT jfrog_xray_scans_pkey PRIMARY KEY (agent_id, workspace_id);
|
||||
UniqueLicensesJWTKey UniqueConstraint = "licenses_jwt_key" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_jwt_key UNIQUE (jwt);
|
||||
UniqueLicensesPkey UniqueConstraint = "licenses_pkey" // ALTER TABLE ONLY licenses ADD CONSTRAINT licenses_pkey PRIMARY KEY (id);
|
||||
UniqueOauth2ProviderAppSecretsAppIDHashedSecretKey UniqueConstraint = "oauth2_provider_app_secrets_app_id_hashed_secret_key" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_app_id_hashed_secret_key UNIQUE (app_id, hashed_secret);
|
||||
UniqueOauth2ProviderAppCodesPkey UniqueConstraint = "oauth2_provider_app_codes_pkey" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_pkey PRIMARY KEY (id);
|
||||
UniqueOauth2ProviderAppCodesSecretPrefixKey UniqueConstraint = "oauth2_provider_app_codes_secret_prefix_key" // ALTER TABLE ONLY oauth2_provider_app_codes ADD CONSTRAINT oauth2_provider_app_codes_secret_prefix_key UNIQUE (secret_prefix);
|
||||
UniqueOauth2ProviderAppSecretsPkey UniqueConstraint = "oauth2_provider_app_secrets_pkey" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_pkey PRIMARY KEY (id);
|
||||
UniqueOauth2ProviderAppSecretsSecretPrefixKey UniqueConstraint = "oauth2_provider_app_secrets_secret_prefix_key" // ALTER TABLE ONLY oauth2_provider_app_secrets ADD CONSTRAINT oauth2_provider_app_secrets_secret_prefix_key UNIQUE (secret_prefix);
|
||||
UniqueOauth2ProviderAppTokensHashPrefixKey UniqueConstraint = "oauth2_provider_app_tokens_hash_prefix_key" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_hash_prefix_key UNIQUE (hash_prefix);
|
||||
UniqueOauth2ProviderAppTokensPkey UniqueConstraint = "oauth2_provider_app_tokens_pkey" // ALTER TABLE ONLY oauth2_provider_app_tokens ADD CONSTRAINT oauth2_provider_app_tokens_pkey PRIMARY KEY (id);
|
||||
UniqueOauth2ProviderAppsNameKey UniqueConstraint = "oauth2_provider_apps_name_key" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_name_key UNIQUE (name);
|
||||
UniqueOauth2ProviderAppsPkey UniqueConstraint = "oauth2_provider_apps_pkey" // ALTER TABLE ONLY oauth2_provider_apps ADD CONSTRAINT oauth2_provider_apps_pkey PRIMARY KEY (id);
|
||||
UniqueOrganizationMembersPkey UniqueConstraint = "organization_members_pkey" // ALTER TABLE ONLY organization_members ADD CONSTRAINT organization_members_pkey PRIMARY KEY (organization_id, user_id);
|
||||
|
||||
@@ -23,16 +23,16 @@ type QueryParamParser struct {
|
||||
// Parsed is a map of all query params that were parsed. This is useful
|
||||
// for checking if extra query params were passed in.
|
||||
Parsed map[string]bool
|
||||
// RequiredParams is a map of all query params that are required. This is useful
|
||||
// RequiredNotEmptyParams is a map of all query params that are required. This is useful
|
||||
// for forcing a value to be provided.
|
||||
RequiredParams map[string]bool
|
||||
RequiredNotEmptyParams map[string]bool
|
||||
}
|
||||
|
||||
func NewQueryParamParser() *QueryParamParser {
|
||||
return &QueryParamParser{
|
||||
Errors: []codersdk.ValidationError{},
|
||||
Parsed: map[string]bool{},
|
||||
RequiredParams: map[string]bool{},
|
||||
Errors: []codersdk.ValidationError{},
|
||||
Parsed: map[string]bool{},
|
||||
RequiredNotEmptyParams: map[string]bool{},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,8 +90,10 @@ func (p *QueryParamParser) Boolean(vals url.Values, def bool, queryParam string)
|
||||
return v
|
||||
}
|
||||
|
||||
func (p *QueryParamParser) Required(queryParam string) *QueryParamParser {
|
||||
p.RequiredParams[queryParam] = true
|
||||
func (p *QueryParamParser) RequiredNotEmpty(queryParam ...string) *QueryParamParser {
|
||||
for _, q := range queryParam {
|
||||
p.RequiredNotEmptyParams[q] = true
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
@@ -121,6 +123,27 @@ func (p *QueryParamParser) UUIDs(vals url.Values, def []uuid.UUID, queryParam st
|
||||
})
|
||||
}
|
||||
|
||||
func (p *QueryParamParser) RedirectURL(vals url.Values, base *url.URL, queryParam string) *url.URL {
|
||||
v, err := parseQueryParam(p, vals, url.Parse, base, queryParam)
|
||||
if err != nil {
|
||||
p.Errors = append(p.Errors, codersdk.ValidationError{
|
||||
Field: queryParam,
|
||||
Detail: fmt.Sprintf("Query param %q must be a valid url: %s", queryParam, err.Error()),
|
||||
})
|
||||
}
|
||||
|
||||
// It can be a sub-directory but not a sub-domain, as we have apps on
|
||||
// sub-domains and that seems too dangerous.
|
||||
if v.Host != base.Host || !strings.HasPrefix(v.Path, base.Path) {
|
||||
p.Errors = append(p.Errors, codersdk.ValidationError{
|
||||
Field: queryParam,
|
||||
Detail: fmt.Sprintf("Query param %q must be a subset of %s", queryParam, base),
|
||||
})
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func (p *QueryParamParser) Time(vals url.Values, def time.Time, queryParam, layout string) time.Time {
|
||||
return p.timeWithMutate(vals, def, queryParam, layout, nil)
|
||||
}
|
||||
@@ -233,10 +256,10 @@ func ParseCustomList[T any](parser *QueryParamParser, vals url.Values, def []T,
|
||||
func parseQueryParam[T any](parser *QueryParamParser, vals url.Values, parse func(v string) (T, error), def T, queryParam string) (T, error) {
|
||||
parser.addParsed(queryParam)
|
||||
// If the query param is required and not present, return an error.
|
||||
if parser.RequiredParams[queryParam] && (!vals.Has(queryParam)) {
|
||||
if parser.RequiredNotEmptyParams[queryParam] && (!vals.Has(queryParam) || vals.Get(queryParam) == "") {
|
||||
parser.Errors = append(parser.Errors, codersdk.ValidationError{
|
||||
Field: queryParam,
|
||||
Detail: fmt.Sprintf("Query param %q is required", queryParam),
|
||||
Detail: fmt.Sprintf("Query param %q is required and cannot be empty", queryParam),
|
||||
})
|
||||
return def, nil
|
||||
}
|
||||
|
||||
@@ -320,9 +320,14 @@ func TestParseQueryParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
parser := httpapi.NewQueryParamParser()
|
||||
parser.Required("test_value")
|
||||
parser.RequiredNotEmpty("test_value")
|
||||
parser.UUID(url.Values{}, uuid.New(), "test_value")
|
||||
require.Len(t, parser.Errors, 1)
|
||||
|
||||
parser = httpapi.NewQueryParamParser()
|
||||
parser.RequiredNotEmpty("test_value")
|
||||
parser.String(url.Values{"test_value": {""}}, "", "test_value")
|
||||
require.Len(t, parser.Errors, 1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
+40
-3
@@ -6,6 +6,8 @@ import (
|
||||
"net/http"
|
||||
"reflect"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/google/uuid"
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
@@ -194,9 +196,44 @@ func ExtractOAuth2ProviderApp(db database.Store) func(http.Handler) http.Handler
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
appID, ok := ParseUUIDParam(rw, r, "app")
|
||||
if !ok {
|
||||
return
|
||||
|
||||
// App can come from a URL param, query param, or form value.
|
||||
paramID := "app"
|
||||
var appID uuid.UUID
|
||||
if chi.URLParam(r, paramID) != "" {
|
||||
var ok bool
|
||||
appID, ok = ParseUUIDParam(rw, r, "app")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
// If not provided by the url, then it is provided according to the
|
||||
// oauth 2 spec. This can occur with query params, or in the body as
|
||||
// form parameters.
|
||||
// This also depends on if you are doing a POST (tokens) or GET (authorize).
|
||||
paramAppID := r.URL.Query().Get("client_id")
|
||||
if paramAppID == "" {
|
||||
// Check the form params!
|
||||
if r.ParseForm() == nil {
|
||||
paramAppID = r.Form.Get("client_id")
|
||||
}
|
||||
}
|
||||
if paramAppID == "" {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Missing OAuth2 client ID.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
appID, err = uuid.Parse(paramAppID)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
|
||||
Message: "Invalid OAuth2 client ID.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
app, err := db.GetOAuth2ProviderAppByID(ctx, appID)
|
||||
|
||||
+6
-6
@@ -72,8 +72,8 @@ func (api *API) insightsUserActivity(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
p := httpapi.NewQueryParamParser().
|
||||
Required("start_time").
|
||||
Required("end_time")
|
||||
RequiredNotEmpty("start_time").
|
||||
RequiredNotEmpty("end_time")
|
||||
vals := r.URL.Query()
|
||||
var (
|
||||
// The QueryParamParser does not preserve timezone, so we need
|
||||
@@ -161,8 +161,8 @@ func (api *API) insightsUserLatency(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
p := httpapi.NewQueryParamParser().
|
||||
Required("start_time").
|
||||
Required("end_time")
|
||||
RequiredNotEmpty("start_time").
|
||||
RequiredNotEmpty("end_time")
|
||||
vals := r.URL.Query()
|
||||
var (
|
||||
// The QueryParamParser does not preserve timezone, so we need
|
||||
@@ -253,8 +253,8 @@ func (api *API) insightsTemplates(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
p := httpapi.NewQueryParamParser().
|
||||
Required("start_time").
|
||||
Required("end_time")
|
||||
RequiredNotEmpty("start_time").
|
||||
RequiredNotEmpty("end_time")
|
||||
vals := r.URL.Query()
|
||||
var (
|
||||
// The QueryParamParser does not preserve timezone, so we need
|
||||
|
||||
+10
-2
@@ -213,12 +213,20 @@ var (
|
||||
Type: "oauth2_app",
|
||||
}
|
||||
|
||||
// ResourceOAuth2ProviderAppSecrets CRUD.
|
||||
// ResourceOAuth2ProviderAppSecret CRUD.
|
||||
// create/delete = Make or delete an OAuth2 app secret.
|
||||
// update = Update last used date.
|
||||
// read = Read OAuth2 app hashed or truncated secret.
|
||||
ResourceOAuth2ProviderAppSecret = Object{
|
||||
Type: "oauth2_app_secrets",
|
||||
Type: "oauth2_app_secret",
|
||||
}
|
||||
|
||||
// ResourceOAuth2ProviderAppCodeToken CRUD.
|
||||
// create/delete = Make or delete an OAuth2 app code or token.
|
||||
// update = None
|
||||
// read = Check if OAuth2 app code or token exists.
|
||||
ResourceOAuth2ProviderAppCodeToken = Object{
|
||||
Type: "oauth2_app_code_token",
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ func AllResources() []Object {
|
||||
ResourceGroup,
|
||||
ResourceLicense,
|
||||
ResourceOAuth2ProviderApp,
|
||||
ResourceOAuth2ProviderAppCodeToken,
|
||||
ResourceOAuth2ProviderAppSecret,
|
||||
ResourceOrgRoleAssignment,
|
||||
ResourceOrganization,
|
||||
|
||||
@@ -148,6 +148,8 @@ func ReloadBuiltinRoles(opts *RoleOptions) {
|
||||
ResourceRoleAssignment.Type: {ActionRead},
|
||||
// All users can see the provisioner daemons.
|
||||
ResourceProvisionerDaemon.Type: {ActionRead},
|
||||
// All users can see OAuth2 provider applications.
|
||||
ResourceOAuth2ProviderApp.Type: {ActionRead},
|
||||
}),
|
||||
Org: map[string][]Permission{},
|
||||
User: append(allPermsExcept(ResourceWorkspaceDormant, ResourceUser, ResourceOrganizationMember),
|
||||
|
||||
+1
-1
@@ -583,7 +583,7 @@ func (api *API) userByName(rw http.ResponseWriter, r *http.Request) {
|
||||
func (api *API) userAutofillParameters(rw http.ResponseWriter, r *http.Request) {
|
||||
user := httpmw.UserParam(r)
|
||||
|
||||
p := httpapi.NewQueryParamParser().Required("template_id")
|
||||
p := httpapi.NewQueryParamParser().RequiredNotEmpty("template_id")
|
||||
templateID := p.UUID(r.URL.Query(), uuid.UUID{}, "template_id")
|
||||
p.ErrorExcessParams(r.URL.Query())
|
||||
if len(p.Errors) > 0 {
|
||||
|
||||
@@ -636,7 +636,7 @@ func (s *Server) workspaceAgentPTY(rw http.ResponseWriter, r *http.Request) {
|
||||
|
||||
values := r.URL.Query()
|
||||
parser := httpapi.NewQueryParamParser()
|
||||
reconnect := parser.Required("reconnect").UUID(values, uuid.New(), "reconnect")
|
||||
reconnect := parser.RequiredNotEmpty("reconnect").UUID(values, uuid.New(), "reconnect")
|
||||
height := parser.UInt(values, 80, "height")
|
||||
width := parser.UInt(values, 80, "width")
|
||||
if len(parser.Errors) > 0 {
|
||||
|
||||
Reference in New Issue
Block a user