Merge pull request #398 from openziti/oauth-testing
OAuth for Public Frontends (#45, #404)
2
.gitignore
vendored
@ -4,7 +4,7 @@
|
||||
*.db
|
||||
automated-release-build
|
||||
etc/dev.yml
|
||||
etc/dev-metrics.yml
|
||||
etc/dev-frontend.yml
|
||||
|
||||
# Dependencies
|
||||
/node_modules/
|
||||
|
@ -1,3 +1,9 @@
|
||||
# v0.4.7
|
||||
|
||||
FEATURE: OAuth authentication with the ability to restrict authenticated users to specified domains for `zrok share public`. Supports both Google and GitHub authentication in this version. More authentication providers, and extensibility to come in future `zrok` releases. See the OAuth configuration guide at `docs/guides/self-hosting/oauth/configuring-oauth.md` for details (https://github.com/openziti/zrok/issues/45, https://github.com/openziti/zrok/issues/404)
|
||||
|
||||
CHANGE: `--basic-auth` realm now presented as the share token rather than as `zrok` in `publicProxy` frontend implementation
|
||||
|
||||
# v0.4.6
|
||||
|
||||
FEATURE: New `--backend-mode caddy`, which pre-processes a `Caddyfile` allowing a `bind` statement to work like this: `bind {{ .ZrokBindAddress }}`. Allows development of complicated API gateways and multi-backend shares, while maintaining the simple, ephemeral sharing model provided by `zrok` (https://github.com/openziti/zrok/issues/391)
|
||||
|
@ -83,7 +83,7 @@ func (cmd *reserveCommand) run(_ *cobra.Command, args []string) {
|
||||
req := &sdk.ShareRequest{
|
||||
BackendMode: sdk.BackendMode(cmd.backendMode),
|
||||
ShareMode: shareMode,
|
||||
Auth: cmd.basicAuth,
|
||||
BasicAuth: cmd.basicAuth,
|
||||
Target: target,
|
||||
}
|
||||
if shareMode == sdk.PublicShareMode {
|
||||
|
@ -99,7 +99,7 @@ func (cmd *sharePrivateCommand) run(_ *cobra.Command, args []string) {
|
||||
req := &sdk.ShareRequest{
|
||||
BackendMode: sdk.BackendMode(cmd.backendMode),
|
||||
ShareMode: sdk.PrivateShareMode,
|
||||
Auth: cmd.basicAuth,
|
||||
BasicAuth: cmd.basicAuth,
|
||||
Target: target,
|
||||
}
|
||||
shr, err := sdk.CreateShare(root, req)
|
||||
|
@ -15,6 +15,7 @@ import (
|
||||
"os/signal"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -22,12 +23,15 @@ func init() {
|
||||
}
|
||||
|
||||
type sharePublicCommand struct {
|
||||
basicAuth []string
|
||||
frontendSelection []string
|
||||
backendMode string
|
||||
headless bool
|
||||
insecure bool
|
||||
cmd *cobra.Command
|
||||
basicAuth []string
|
||||
frontendSelection []string
|
||||
backendMode string
|
||||
headless bool
|
||||
insecure bool
|
||||
oauthProvider string
|
||||
oauthEmailDomains []string
|
||||
oauthCheckInterval time.Duration
|
||||
cmd *cobra.Command
|
||||
}
|
||||
|
||||
func newSharePublicCommand() *sharePublicCommand {
|
||||
@ -37,11 +41,17 @@ func newSharePublicCommand() *sharePublicCommand {
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
command := &sharePublicCommand{cmd: cmd}
|
||||
cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (<username:password>,...)")
|
||||
cmd.Flags().StringArrayVar(&command.frontendSelection, "frontends", []string{"public"}, "Selected frontends to use for the share")
|
||||
cmd.Flags().StringVarP(&command.backendMode, "backend-mode", "b", "proxy", "The backend mode {proxy, web, caddy}")
|
||||
cmd.Flags().BoolVar(&command.headless, "headless", false, "Disable TUI and run headless")
|
||||
cmd.Flags().BoolVar(&command.insecure, "insecure", false, "Enable insecure TLS certificate validation for <target>")
|
||||
|
||||
cmd.Flags().StringArrayVar(&command.basicAuth, "basic-auth", []string{}, "Basic authentication users (<username:password>,...)")
|
||||
cmd.Flags().StringVar(&command.oauthProvider, "oauth-provider", "", "Enable OAuth provider [google, github]")
|
||||
cmd.Flags().StringArrayVar(&command.oauthEmailDomains, "oauth-email-domains", []string{}, "Allow only these email domains to authenticate via OAuth")
|
||||
cmd.Flags().DurationVar(&command.oauthCheckInterval, "oauth-check-interval", 3*time.Hour, "Maximum lifetime for OAuth authentication; reauthenticate after expiry")
|
||||
cmd.MarkFlagsMutuallyExclusive("basic-auth", "oauth-provider")
|
||||
|
||||
cmd.Run = command.run
|
||||
return command
|
||||
}
|
||||
@ -95,9 +105,14 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) {
|
||||
BackendMode: sdk.BackendMode(cmd.backendMode),
|
||||
ShareMode: sdk.PublicShareMode,
|
||||
Frontends: cmd.frontendSelection,
|
||||
Auth: cmd.basicAuth,
|
||||
BasicAuth: cmd.basicAuth,
|
||||
Target: target,
|
||||
}
|
||||
if cmd.oauthProvider != "" {
|
||||
req.OauthProvider = cmd.oauthProvider
|
||||
req.OauthEmailDomains = cmd.oauthEmailDomains
|
||||
req.OauthAuthorizationCheckInterval = cmd.oauthCheckInterval
|
||||
}
|
||||
shr, err := sdk.CreateShare(root, req)
|
||||
if err != nil {
|
||||
if !panicInstead {
|
||||
|
@ -20,10 +20,12 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
var cfg *config.Config
|
||||
var str *store.Store
|
||||
var idb influxdb2.Client
|
||||
var limitsAgent *limits.Agent
|
||||
var (
|
||||
cfg *config.Config
|
||||
str *store.Store
|
||||
idb influxdb2.Client
|
||||
limitsAgent *limits.Agent
|
||||
)
|
||||
|
||||
func Run(inCfg *config.Config) error {
|
||||
cfg = inCfg
|
||||
|
@ -14,11 +14,24 @@ func newPrivateResourceAllocator() *privateResourceAllocator {
|
||||
}
|
||||
|
||||
func (a *privateResourceAllocator) allocate(envZId, shrToken string, params share.ShareParams, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, frontendEndpoints []string, err error) {
|
||||
var authUsers []*sdk.AuthUser
|
||||
var authUsers []*sdk.AuthUserConfig
|
||||
for _, authUser := range params.Body.AuthUsers {
|
||||
authUsers = append(authUsers, &sdk.AuthUser{authUser.Username, authUser.Password})
|
||||
authUsers = append(authUsers, &sdk.AuthUserConfig{Username: authUser.Username, Password: authUser.Password})
|
||||
}
|
||||
cfgZId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, params.Body.AuthScheme, authUsers, edge)
|
||||
authScheme, err := sdk.ParseAuthScheme(params.Body.AuthScheme)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
options := &zrokEdgeSdk.FrontendOptions{
|
||||
AuthScheme: authScheme,
|
||||
BasicAuthUsers: authUsers,
|
||||
Oauth: &sdk.OauthConfig{
|
||||
Provider: params.Body.OauthProvider,
|
||||
EmailDomains: params.Body.OauthEmailDomains,
|
||||
AuthorizationCheckInterval: params.Body.OauthAuthorizationCheckInterval,
|
||||
},
|
||||
}
|
||||
cfgZId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, options, edge)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
@ -14,11 +14,24 @@ func newPublicResourceAllocator() *publicResourceAllocator {
|
||||
}
|
||||
|
||||
func (a *publicResourceAllocator) allocate(envZId, shrToken string, frontendZIds, frontendTemplates []string, params share.ShareParams, edge *rest_management_api_client.ZitiEdgeManagement) (shrZId string, frontendEndpoints []string, err error) {
|
||||
var authUsers []*sdk.AuthUser
|
||||
var authUsers []*sdk.AuthUserConfig
|
||||
for _, authUser := range params.Body.AuthUsers {
|
||||
authUsers = append(authUsers, &sdk.AuthUser{authUser.Username, authUser.Password})
|
||||
authUsers = append(authUsers, &sdk.AuthUserConfig{Username: authUser.Username, Password: authUser.Password})
|
||||
}
|
||||
cfgId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, params.Body.AuthScheme, authUsers, edge)
|
||||
authScheme, err := sdk.ParseAuthScheme(params.Body.AuthScheme)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
options := &zrokEdgeSdk.FrontendOptions{
|
||||
AuthScheme: authScheme,
|
||||
BasicAuthUsers: authUsers,
|
||||
Oauth: &sdk.OauthConfig{
|
||||
Provider: params.Body.OauthProvider,
|
||||
EmailDomains: params.Body.OauthEmailDomains,
|
||||
AuthorizationCheckInterval: params.Body.OauthAuthorizationCheckInterval,
|
||||
},
|
||||
}
|
||||
cfgId, err := zrokEdgeSdk.CreateConfig(zrokProxyConfigId, envZId, shrToken, options, edge)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
@ -11,18 +11,27 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func CreateConfig(cfgTypeZId, envZId, shrToken string, authSchemeStr string, authUsers []*sdk.AuthUser, edge *rest_management_api_client.ZitiEdgeManagement) (cfgZId string, err error) {
|
||||
authScheme, err := sdk.ParseAuthScheme(authSchemeStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
cfg := &sdk.ProxyConfig{
|
||||
AuthScheme: authScheme,
|
||||
type FrontendOptions struct {
|
||||
AuthScheme sdk.AuthScheme
|
||||
BasicAuthUsers []*sdk.AuthUserConfig
|
||||
Oauth *sdk.OauthConfig
|
||||
}
|
||||
|
||||
func CreateConfig(cfgTypeZId, envZId, shrToken string, options *FrontendOptions, edge *rest_management_api_client.ZitiEdgeManagement) (cfgZId string, err error) {
|
||||
cfg := &sdk.FrontendConfig{
|
||||
AuthScheme: options.AuthScheme,
|
||||
}
|
||||
if cfg.AuthScheme == sdk.Basic {
|
||||
cfg.BasicAuth = &sdk.BasicAuth{}
|
||||
for _, authUser := range authUsers {
|
||||
cfg.BasicAuth.Users = append(cfg.BasicAuth.Users, &sdk.AuthUser{Username: authUser.Username, Password: authUser.Password})
|
||||
cfg.BasicAuth = &sdk.BasicAuthConfig{}
|
||||
for _, authUser := range options.BasicAuthUsers {
|
||||
cfg.BasicAuth.Users = append(cfg.BasicAuth.Users, &sdk.AuthUserConfig{Username: authUser.Username, Password: authUser.Password})
|
||||
}
|
||||
}
|
||||
if cfg.AuthScheme == sdk.Oauth && options.Oauth != nil {
|
||||
cfg.OauthAuth = &sdk.OauthConfig{
|
||||
Provider: options.Oauth.Provider,
|
||||
EmailDomains: options.Oauth.EmailDomains,
|
||||
AuthorizationCheckInterval: options.Oauth.AuthorizationCheckInterval,
|
||||
}
|
||||
}
|
||||
cfgCrt := &rest_model.ConfigCreate{
|
||||
|
7
docs/guides/self-hosting/oauth/_category_.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "OAuth",
|
||||
"position": 70,
|
||||
"link": {
|
||||
"type": "generated-index"
|
||||
}
|
||||
}
|
154
docs/guides/self-hosting/oauth/configuring-oauth.md
Normal file
@ -0,0 +1,154 @@
|
||||
# OAuth Public Frontend Configuration
|
||||
|
||||
As of `v0.4.7`, `zrok` includes OAuth integration for both Google and GitHub for `zrok access public` public frontends.
|
||||
|
||||
This integration allows you to create public shares and request that the public frontend authenticate your users against either the Google or GitHub OAuth endpoints (using the user's Google or GitHub accounts). Additionally, you can restrict the email address domain associated with the count to a list of domains that you provide when you create the share.
|
||||
|
||||
This is a first step towards a more comprehensive portfolio of user authentication strategies in future `zrok` releases.
|
||||
|
||||
## Planning for the OAuth Frontend
|
||||
|
||||
The current implementation of the OAuth public frontend uses a HTTP listener to handle redirects from OAuth providers. You'll need to configure a DNS name and a port for this listener that is accessible by your end users. We'll refer to this listener as the "OAuth frontend" in this guide.
|
||||
|
||||
We'll use the public DNS address of the OAuth frontend when creating the Google and GitHub OAuth clients below. This address is typically configured into these clients as the "redirect URL" where these clients will send the authenticated users after authentication.
|
||||
|
||||
The `zrok` OAuth frontend will capture the successful authentication and forward the user back to their original destination.
|
||||
|
||||
## Configuring a Google OAuth Client ID
|
||||
|
||||
### OAuth Content Screen
|
||||
|
||||
Before you can configure an OAuth Client ID in Google Cloud, you have to configure the "OAuth content screen".
|
||||
|
||||
In the Google Cloud console, navigate to: `APIs & Services > Credentials > OAuth content screen`
|
||||
|
||||
![](images/google_oauth_content_screen_2.png)
|
||||
|
||||
Here you can give your `zrok` public frontend an identity and branding to match your deployment.
|
||||
|
||||
![](images/google_oauth_content_screen_3.png)
|
||||
|
||||
Describe what domains are authorized to access your public frontend and establish contact information.
|
||||
|
||||
![](images/google_oauth_content_screen_4.png)
|
||||
|
||||
Add a non-sensitive scope for `../auth/userinfo.email`. This is important as it allows the `zrok` OAuth frontend to receive the email address of the authenticated user.
|
||||
|
||||
![](images/google_oauth_content_screen_5.png)
|
||||
|
||||
![](images/google_oauth_content_screen_6.png)
|
||||
|
||||
Now your OAuth content screen is configured.
|
||||
|
||||
### Create the OAuth 2.0 Client ID
|
||||
|
||||
Next we create the OAuth Client ID for your public frontend.
|
||||
|
||||
In the Google Cloud Console, navigate to: `APIs & Services > Credentials > + Create Credentials`
|
||||
|
||||
![](images/google_create_credentials_1.png)
|
||||
|
||||
Select `OAuth client ID` from the `+ Create Credentials` dropdown.
|
||||
|
||||
![](images/google_create_credentials_2.png)
|
||||
|
||||
Application type is `Web Application`.
|
||||
|
||||
![](images/google_create_credentials_3.png)
|
||||
|
||||
The most important bit here is the "Authorized redirect URIs". You're going to want to put a URL here that matches the `zrok` OAuth frontend address that you configured at the start of this guide, but at the end of the URL you're going to append `/google/oauth` to the URL.
|
||||
|
||||
![](images/google_create_credentials_4.png)
|
||||
|
||||
Save the client ID and the client secret. You'll configure these into your `frontend.yml`.
|
||||
|
||||
With this your Google OAuth client should be configured and ready.
|
||||
|
||||
## Configuring a GitHub Client ID
|
||||
|
||||
Register a new OAuth application through the GitHub settings for the account that owns the application.
|
||||
|
||||
Navigate to:`Settings > Developer Settings > OAuth Apps > Register a new application`
|
||||
|
||||
![](images/github_create_oauth_application_1.png)
|
||||
|
||||
![](images/github_create_oauth_application_2.png)
|
||||
|
||||
The "Authorized callback URL" should be configured to match the OAuth frontend address you configured at the start of this guide, with `/github/oauth` appended to the end.
|
||||
|
||||
![](images/github_create_oauth_application_3.png)
|
||||
|
||||
Create a new client secret.
|
||||
|
||||
![](images/github_create_oauth_application_4.png)
|
||||
|
||||
Save the client ID and the client secret. You'll configure these into your `frontend.yml`.
|
||||
|
||||
## Configuring your Public Frontend
|
||||
|
||||
The public frontend configuration includes a new `oauth` section:
|
||||
|
||||
```yaml
|
||||
oauth:
|
||||
redirect_host: oauth.zrok.io
|
||||
redirect_port: 28080
|
||||
redirect_http_only: false
|
||||
hash_key: "<yourRandomHashKey>"
|
||||
providers:
|
||||
- name: google
|
||||
client_id: <client-id>
|
||||
client_secret: <client-secret>
|
||||
- name: github
|
||||
client_id: <client-id>
|
||||
client_secret: <client-secret>
|
||||
|
||||
```
|
||||
|
||||
The `redirect_host` and `redirect_port` value should correspond with the DNS hostname and port configured as your OAuth frontend.
|
||||
|
||||
The `redirect_http_only` is useful in development environments where your OAuth frontend is not running behind an HTTPS reverse proxy. Should not be enabled in production environments!
|
||||
|
||||
`hash_key` is a unique string for your installation that is used to secure the authentication payloads for your public frontend.
|
||||
|
||||
`providers` is a list of configured providers for this public frontend. The current implementation supports `google` and `github` as options.
|
||||
|
||||
Both the `google` and `github` providers accept a `client_id` and `client_secret` parameter. These values are provided when you configure the OAuth clients at Google or GitHub.
|
||||
|
||||
## Enabling OAuth on a Public Share
|
||||
|
||||
With your public frontend configured to support OAuth, you can test this by creating a public share. There are new command line options to support this:
|
||||
|
||||
```
|
||||
$ zrok share public
|
||||
Error: accepts 1 arg(s), received 0
|
||||
Usage:
|
||||
zrok share public <target> [flags]
|
||||
|
||||
Flags:
|
||||
-b, --backend-mode string The backend mode {proxy, web, caddy} (default "proxy")
|
||||
--basic-auth stringArray Basic authentication users (<username:password>,...)
|
||||
--frontends stringArray Selected frontends to use for the share (default [public])
|
||||
--headless Disable TUI and run headless
|
||||
-h, --help help for public
|
||||
--insecure Enable insecure TLS certificate validation for <target>
|
||||
--oauth-check-interval duration Maximum lifetime for OAuth authentication; reauthenticate after expiry (default 3h0m0s)
|
||||
--oauth-email-domains stringArray Allow only these email domains to authenticate via OAuth
|
||||
--oauth-provider string Enable OAuth provider [google, github]
|
||||
|
||||
Global Flags:
|
||||
-p, --panic Panic instead of showing pretty errors
|
||||
-v, --verbose Enable verbose logging
|
||||
```
|
||||
|
||||
The `--oauth-provider` flag enables OAuth for the share using the specified provider.
|
||||
|
||||
The `--oauth-email-domains` flag accepts a comma-separated list of authenticated email address domains that are allowed to access the share.
|
||||
|
||||
The `--oauth-check-interval` flag specifies how frequently the authentication must be checked.
|
||||
|
||||
An example public share:
|
||||
|
||||
```
|
||||
$ zrok share public --backend-mode web --oauth-provider github --oauth-email-domains zrok.io ~/public
|
||||
```
|
||||
|
BIN
docs/guides/self-hosting/oauth/images/github_create_oauth_application_1.png
Executable file
After Width: | Height: | Size: 42 KiB |
BIN
docs/guides/self-hosting/oauth/images/github_create_oauth_application_2.png
Executable file
After Width: | Height: | Size: 61 KiB |
BIN
docs/guides/self-hosting/oauth/images/github_create_oauth_application_3.png
Executable file
After Width: | Height: | Size: 70 KiB |
BIN
docs/guides/self-hosting/oauth/images/github_create_oauth_application_4.png
Executable file
After Width: | Height: | Size: 90 KiB |
BIN
docs/guides/self-hosting/oauth/images/github_create_oauth_application_5.png
Executable file
After Width: | Height: | Size: 78 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_create_credentials_1.png
Executable file
After Width: | Height: | Size: 82 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_create_credentials_2.png
Executable file
After Width: | Height: | Size: 66 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_create_credentials_3.png
Executable file
After Width: | Height: | Size: 109 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_create_credentials_4.png
Executable file
After Width: | Height: | Size: 136 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_oauth_content_screen_1.png
Executable file
After Width: | Height: | Size: 90 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_oauth_content_screen_2.png
Executable file
After Width: | Height: | Size: 212 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_oauth_content_screen_3.png
Executable file
After Width: | Height: | Size: 206 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_oauth_content_screen_4.png
Executable file
After Width: | Height: | Size: 110 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_oauth_content_screen_5.png
Executable file
After Width: | Height: | Size: 93 KiB |
BIN
docs/guides/self-hosting/oauth/images/google_oauth_content_screen_6.png
Executable file
After Width: | Height: | Size: 95 KiB |
@ -1,14 +1,43 @@
|
||||
package publicProxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/michaelquigley/cf"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
zhttp "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Identity string
|
||||
Address string
|
||||
HostMatch string
|
||||
Oauth *OauthConfig
|
||||
}
|
||||
|
||||
type OauthConfig struct {
|
||||
RedirectHost string
|
||||
RedirectPort int
|
||||
RedirectHttpOnly bool
|
||||
HashKey string `cf:"+secret"`
|
||||
Providers []*OauthProviderConfig
|
||||
}
|
||||
|
||||
func (oc *OauthConfig) GetProvider(name string) *OauthProviderConfig {
|
||||
for _, provider := range oc.Providers {
|
||||
if provider.Name == name {
|
||||
return provider
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type OauthProviderConfig struct {
|
||||
Name string
|
||||
ClientId string
|
||||
ClientSecret string `cf:"+secret"`
|
||||
}
|
||||
|
||||
func DefaultConfig() *Config {
|
||||
@ -24,3 +53,18 @@ func (c *Config) Load(path string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func configureOauthHandlers(ctx context.Context, cfg *Config, tls bool) error {
|
||||
if cfg.Oauth == nil {
|
||||
logrus.Info("no oauth configuration; skipping oauth handler startup")
|
||||
return nil
|
||||
}
|
||||
if err := configureGoogleOauth(cfg.Oauth, tls); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := configureGithubOauth(cfg.Oauth, tls); err != nil {
|
||||
return err
|
||||
}
|
||||
zhttp.StartServer(ctx, fmt.Sprintf("%s:%d", strings.Split(cfg.Address, ":")[0], cfg.Oauth.RedirectPort))
|
||||
return nil
|
||||
}
|
||||
|
186
endpoints/publicProxy/github.go
Normal file
@ -0,0 +1,186 @@
|
||||
package publicProxy
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/zitadel/oidc/v2/pkg/client/rp"
|
||||
zhttp "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
githubOAuth "golang.org/x/oauth2/github"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func configureGithubOauth(cfg *OauthConfig, tls bool) error {
|
||||
scheme := "http"
|
||||
if tls {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
providerCfg := cfg.GetProvider("github")
|
||||
if providerCfg == nil {
|
||||
logrus.Info("unable to find provider config for github; skipping")
|
||||
return nil
|
||||
}
|
||||
clientID := providerCfg.ClientId
|
||||
callbackPath := "/github/oauth"
|
||||
redirectUrl := fmt.Sprintf("%s://%s", scheme, cfg.RedirectHost)
|
||||
rpConfig := &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: providerCfg.ClientSecret,
|
||||
RedirectURL: fmt.Sprintf("%v:%v%v", redirectUrl, cfg.RedirectPort, callbackPath),
|
||||
Scopes: []string{"user:email"},
|
||||
Endpoint: githubOAuth.Endpoint,
|
||||
}
|
||||
|
||||
hash := md5.New()
|
||||
n, err := hash.Write([]byte(cfg.HashKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(cfg.HashKey) {
|
||||
return errors.New("short hash")
|
||||
}
|
||||
key := hash.Sum(nil)
|
||||
|
||||
u, err := url.Parse(redirectUrl)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to parse redirect url: %v", err)
|
||||
return err
|
||||
}
|
||||
parts := strings.Split(u.Hostname(), ".")
|
||||
domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
|
||||
|
||||
cookieHandler := zhttp.NewCookieHandler(key, key, zhttp.WithUnsecure(), zhttp.WithDomain(domain))
|
||||
|
||||
options := []rp.Option{
|
||||
rp.WithCookieHandler(cookieHandler),
|
||||
rp.WithVerifierOpts(rp.WithIssuedAtOffset(5 * time.Second)),
|
||||
//rp.WithPKCE(cookieHandler), //Github currently doesn't support pkce. Update when that changes.
|
||||
}
|
||||
|
||||
relyingParty, err := rp.NewRelyingPartyOAuth(rpConfig, options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type IntermediateJWT struct {
|
||||
State string `json:"state"`
|
||||
Host string `json:"host"`
|
||||
AuthorizationCheckInterval string `json:"authorizationCheckInterval"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type githubUserResp struct {
|
||||
Email string
|
||||
Primary bool
|
||||
Verified bool
|
||||
Visibility string
|
||||
}
|
||||
|
||||
authHandlerWithQueryState := func(party rp.RelyingParty) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
host, err := url.QueryUnescape(r.URL.Query().Get("targethost"))
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to unescape target host: %v", err)
|
||||
}
|
||||
rp.AuthURLHandler(func() string {
|
||||
id := uuid.New().String()
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodHS256, IntermediateJWT{
|
||||
id,
|
||||
host,
|
||||
r.URL.Query().Get("checkInterval"),
|
||||
jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "zrok",
|
||||
Subject: "intermediate_token",
|
||||
ID: id,
|
||||
},
|
||||
})
|
||||
s, err := t.SignedString(key)
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to sign intermediate JWT: %v", err)
|
||||
}
|
||||
return s
|
||||
}, party, rp.WithURLParam("access_type", "offline"))(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
http.Handle("/github/login", authHandlerWithQueryState(relyingParty))
|
||||
getEmail := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[*oidc.IDTokenClaims], state string, rp rp.RelyingParty) {
|
||||
parsedUrl, err := url.Parse("https://api.github.com/user/emails")
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
req := &http.Request{
|
||||
Method: http.MethodGet,
|
||||
URL: parsedUrl,
|
||||
Header: make(http.Header),
|
||||
}
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", tokens.AccessToken))
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
logrus.Error("Error getting user info from github: " + err.Error() + "\n")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
response, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error reading response body: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
var rDat []githubUserResp
|
||||
err = json.Unmarshal(response, &rDat)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error unmarshalling google oauth response: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
primaryEmail := ""
|
||||
for _, email := range rDat {
|
||||
if email.Primary {
|
||||
primaryEmail = email.Email
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
token, err := jwt.ParseWithClaims(state, &IntermediateJWT{}, func(t *jwt.Token) (interface{}, error) {
|
||||
return key, nil
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("After intermediate token parse: %v", err.Error()), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
authCheckInterval := 3 * time.Hour
|
||||
i, err := time.ParseDuration(token.Claims.(*IntermediateJWT).AuthorizationCheckInterval)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to parse authorization check interval: %v. Defaulting to 3 hours", err)
|
||||
} else {
|
||||
authCheckInterval = i
|
||||
}
|
||||
|
||||
SetZrokCookie(w, domain, primaryEmail, tokens.AccessToken, "github", authCheckInterval, key)
|
||||
http.Redirect(w, r, fmt.Sprintf("%s://%s", scheme, token.Claims.(*IntermediateJWT).Host), http.StatusFound)
|
||||
}
|
||||
|
||||
http.Handle(callbackPath, rp.CodeExchangeHandler(getEmail, relyingParty))
|
||||
return nil
|
||||
}
|
166
endpoints/publicProxy/google.go
Normal file
@ -0,0 +1,166 @@
|
||||
package publicProxy
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/zitadel/oidc/v2/pkg/client/rp"
|
||||
zhttp "github.com/zitadel/oidc/v2/pkg/http"
|
||||
"github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
"golang.org/x/oauth2"
|
||||
googleOauth "golang.org/x/oauth2/google"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func configureGoogleOauth(cfg *OauthConfig, tls bool) error {
|
||||
scheme := "http"
|
||||
if tls {
|
||||
scheme = "https"
|
||||
}
|
||||
|
||||
providerCfg := cfg.GetProvider("google")
|
||||
if providerCfg == nil {
|
||||
logrus.Info("unable to find provider config for google. Skipping.")
|
||||
return nil
|
||||
}
|
||||
|
||||
clientID := providerCfg.ClientId
|
||||
callbackPath := "/google/oauth"
|
||||
redirectUrl := fmt.Sprintf("%s://%s", scheme, cfg.RedirectHost)
|
||||
rpConfig := &oauth2.Config{
|
||||
ClientID: clientID,
|
||||
ClientSecret: providerCfg.ClientSecret,
|
||||
RedirectURL: fmt.Sprintf("%v:%v%v", redirectUrl, cfg.RedirectPort, callbackPath),
|
||||
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email"},
|
||||
Endpoint: googleOauth.Endpoint,
|
||||
}
|
||||
|
||||
hash := md5.New()
|
||||
n, err := hash.Write([]byte(cfg.HashKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(cfg.HashKey) {
|
||||
return errors.New("short hash")
|
||||
}
|
||||
key := hash.Sum(nil)
|
||||
|
||||
u, err := url.Parse(redirectUrl)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to parse redirect url: %v", err)
|
||||
return err
|
||||
}
|
||||
parts := strings.Split(u.Hostname(), ".")
|
||||
domain := parts[len(parts)-2] + "." + parts[len(parts)-1]
|
||||
|
||||
cookieHandler := zhttp.NewCookieHandler(key, key, zhttp.WithUnsecure(), zhttp.WithDomain(domain))
|
||||
|
||||
options := []rp.Option{
|
||||
rp.WithCookieHandler(cookieHandler),
|
||||
rp.WithVerifierOpts(rp.WithIssuedAtOffset(5 * time.Second)),
|
||||
rp.WithPKCE(cookieHandler),
|
||||
}
|
||||
|
||||
relyingParty, err := rp.NewRelyingPartyOAuth(rpConfig, options...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
type IntermediateJWT struct {
|
||||
State string `json:"state"`
|
||||
Host string `json:"host"`
|
||||
AuthorizationCheckInterval string `json:"authorizationCheckInterval"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
type googleOauthEmailResp struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
authHandlerWithQueryState := func(party rp.RelyingParty) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
host, err := url.QueryUnescape(r.URL.Query().Get("targethost"))
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to unescape target host: %v", err)
|
||||
}
|
||||
rp.AuthURLHandler(func() string {
|
||||
id := uuid.New().String()
|
||||
t := jwt.NewWithClaims(jwt.SigningMethodHS256, IntermediateJWT{
|
||||
id,
|
||||
host,
|
||||
r.URL.Query().Get("checkInterval"),
|
||||
jwt.RegisteredClaims{
|
||||
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
|
||||
IssuedAt: jwt.NewNumericDate(time.Now()),
|
||||
NotBefore: jwt.NewNumericDate(time.Now()),
|
||||
Issuer: "zrok",
|
||||
Subject: "intermediate_token",
|
||||
ID: id,
|
||||
},
|
||||
})
|
||||
s, err := t.SignedString(key)
|
||||
if err != nil {
|
||||
logrus.Errorf("Unable to sign intermediate JWT: %v", err)
|
||||
}
|
||||
return s
|
||||
}, party, rp.WithURLParam("access_type", "offline"))(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
http.Handle("/google/login", authHandlerWithQueryState(relyingParty))
|
||||
getEmail := func(w http.ResponseWriter, r *http.Request, tokens *oidc.Tokens[*oidc.IDTokenClaims], state string, rp rp.RelyingParty) {
|
||||
resp, err := http.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + url.QueryEscape(tokens.AccessToken))
|
||||
if err != nil {
|
||||
logrus.Error("Error getting user info from google: " + err.Error() + "\n")
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
response, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error reading response body: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
logrus.Infof("Response from google userinfo endpoint: %s", string(response))
|
||||
rDat := googleOauthEmailResp{}
|
||||
err = json.Unmarshal(response, &rDat)
|
||||
if err != nil {
|
||||
logrus.Errorf("Error unmarshalling google oauth response: %v", err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := jwt.ParseWithClaims(state, &IntermediateJWT{}, func(t *jwt.Token) (interface{}, error) {
|
||||
return key, nil
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("After intermediate token parse: %v", err.Error()), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
authCheckInterval := 3 * time.Hour
|
||||
i, err := time.ParseDuration(token.Claims.(*IntermediateJWT).AuthorizationCheckInterval)
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to parse authorization check interval: %v. Defaulting to 3 hours", err)
|
||||
} else {
|
||||
authCheckInterval = i
|
||||
}
|
||||
|
||||
SetZrokCookie(w, domain, rDat.Email, tokens.AccessToken, "google", authCheckInterval, key)
|
||||
http.Redirect(w, r, fmt.Sprintf("%s://%s", scheme, token.Claims.(*IntermediateJWT).Host), http.StatusFound)
|
||||
}
|
||||
|
||||
http.Handle(callbackPath, rp.CodeExchangeHandler(getEmail, relyingParty))
|
||||
return nil
|
||||
}
|
@ -2,11 +2,14 @@ package publicProxy
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/openziti/sdk-golang/ziti"
|
||||
"github.com/openziti/zrok/endpoints"
|
||||
"github.com/openziti/zrok/endpoints/publicProxy/healthUi"
|
||||
"github.com/openziti/zrok/endpoints/publicProxy/notFoundUi"
|
||||
"github.com/openziti/zrok/endpoints/publicProxy/unauthorizedUi"
|
||||
"github.com/openziti/zrok/environment"
|
||||
"github.com/openziti/zrok/sdk"
|
||||
"github.com/openziti/zrok/util"
|
||||
@ -17,20 +20,34 @@ import (
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type httpFrontend struct {
|
||||
type HttpFrontend struct {
|
||||
cfg *Config
|
||||
zCtx ziti.Context
|
||||
handler http.Handler
|
||||
}
|
||||
|
||||
func NewHTTP(cfg *Config) (*httpFrontend, error) {
|
||||
env, err := environment.LoadRoot()
|
||||
func NewHTTP(cfg *Config) (*HttpFrontend, error) {
|
||||
var key []byte
|
||||
if cfg.Oauth != nil {
|
||||
hash := md5.New()
|
||||
n, err := hash.Write([]byte(cfg.Oauth.HashKey))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if n != len(cfg.Oauth.HashKey) {
|
||||
return nil, errors.New("short hash")
|
||||
}
|
||||
key = hash.Sum(nil)
|
||||
}
|
||||
|
||||
root, err := environment.LoadRoot()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error loading environment root")
|
||||
}
|
||||
zCfgPath, err := env.ZitiIdentityNamed(cfg.Identity)
|
||||
zCfgPath, err := root.ZitiIdentityNamed(cfg.Identity)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "error getting ziti identity '%v' from environment", cfg.Identity)
|
||||
}
|
||||
@ -52,26 +69,28 @@ func NewHTTP(cfg *Config) (*httpFrontend, error) {
|
||||
return nil, err
|
||||
}
|
||||
proxy.Transport = zTransport
|
||||
|
||||
handler := authHandler(util.NewProxyHandler(proxy), "zrok", cfg, zCtx)
|
||||
return &httpFrontend{
|
||||
if err := configureOauthHandlers(context.Background(), cfg, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
handler := authHandler(util.NewProxyHandler(proxy), cfg, key, zCtx)
|
||||
return &HttpFrontend{
|
||||
cfg: cfg,
|
||||
zCtx: zCtx,
|
||||
handler: handler,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (self *httpFrontend) Run() error {
|
||||
return http.ListenAndServe(self.cfg.Address, self.handler)
|
||||
func (f *HttpFrontend) Run() error {
|
||||
return http.ListenAndServe(f.cfg.Address, f.handler)
|
||||
}
|
||||
|
||||
type zitiDialContext struct {
|
||||
ctx ziti.Context
|
||||
}
|
||||
|
||||
func (self *zitiDialContext) Dial(_ context.Context, _ string, addr string) (net.Conn, error) {
|
||||
func (c *zitiDialContext) Dial(_ context.Context, _ string, addr string) (net.Conn, error) {
|
||||
shrToken := strings.Split(addr, ":")[0] // ignore :port (we get passed 'host:port')
|
||||
conn, err := self.ctx.Dial(shrToken)
|
||||
conn, err := c.ctx.Dial(shrToken)
|
||||
if err != nil {
|
||||
return conn, err
|
||||
}
|
||||
@ -128,9 +147,9 @@ func hostTargetReverseProxy(cfg *Config, ctx ziti.Context) *httputil.ReverseProx
|
||||
return &httputil.ReverseProxy{Director: director}
|
||||
}
|
||||
|
||||
func authHandler(handler http.Handler, realm string, cfg *Config, ctx ziti.Context) http.HandlerFunc {
|
||||
func authHandler(handler http.Handler, pcfg *Config, key []byte, ctx ziti.Context) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
shrToken := resolveService(cfg.HostMatch, r.Host)
|
||||
shrToken := resolveService(pcfg.HostMatch, r.Host)
|
||||
if shrToken != "" {
|
||||
if svc, found := endpoints.GetRefreshedService(shrToken, ctx); found {
|
||||
if cfg, found := svc.Config[sdk.ZrokProxyConfig]; found {
|
||||
@ -145,7 +164,7 @@ func authHandler(handler http.Handler, realm string, cfg *Config, ctx ziti.Conte
|
||||
logrus.Debugf("auth scheme basic '%v", shrToken)
|
||||
inUser, inPass, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
writeUnauthorizedResponse(w, realm)
|
||||
basicAuthRequired(w, shrToken)
|
||||
return
|
||||
}
|
||||
authed := false
|
||||
@ -179,15 +198,96 @@ func authHandler(handler http.Handler, realm string, cfg *Config, ctx ziti.Conte
|
||||
}
|
||||
|
||||
if !authed {
|
||||
writeUnauthorizedResponse(w, realm)
|
||||
basicAuthRequired(w, shrToken)
|
||||
return
|
||||
}
|
||||
|
||||
handler.ServeHTTP(w, r)
|
||||
|
||||
case string(sdk.Oauth):
|
||||
logrus.Debugf("auth scheme oauth '%v'", shrToken)
|
||||
|
||||
if oauthCfg, found := cfg["oauth"]; found {
|
||||
if provider, found := oauthCfg.(map[string]interface{})["provider"]; found {
|
||||
var authCheckInterval time.Duration
|
||||
if checkInterval, found := oauthCfg.(map[string]interface{})["authorization_check_interval"]; !found {
|
||||
logrus.Errorf("Missing authorization check interval in share config. Defaulting to 3 hours")
|
||||
authCheckInterval = 3 * time.Hour
|
||||
} else {
|
||||
i, err := time.ParseDuration(checkInterval.(string))
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to parse authorization check interval in share config (%v). Defaulting to 3 hours", checkInterval)
|
||||
authCheckInterval = 3 * time.Hour
|
||||
} else {
|
||||
authCheckInterval = i
|
||||
}
|
||||
}
|
||||
|
||||
target := fmt.Sprintf("%s%s", r.Host, r.URL.Path)
|
||||
|
||||
cookie, err := r.Cookie("zrok-access")
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to get 'zrok-access' cookie: %v", err)
|
||||
oauthLoginRequired(w, r, shrToken, pcfg, provider.(string), target, authCheckInterval)
|
||||
return
|
||||
}
|
||||
tkn, err := jwt.ParseWithClaims(cookie.Value, &ZrokClaims{}, func(t *jwt.Token) (interface{}, error) {
|
||||
if pcfg.Oauth == nil {
|
||||
return nil, fmt.Errorf("missing oauth configuration for access point; unable to parse jwt")
|
||||
}
|
||||
return key, nil
|
||||
})
|
||||
if err != nil {
|
||||
logrus.Errorf("unable to parse jwt: %v", err)
|
||||
oauthLoginRequired(w, r, shrToken, pcfg, provider.(string), target, authCheckInterval)
|
||||
return
|
||||
}
|
||||
claims := tkn.Claims.(*ZrokClaims)
|
||||
if claims.Provider != provider {
|
||||
logrus.Error("provider mismatch; restarting auth flow")
|
||||
oauthLoginRequired(w, r, shrToken, pcfg, provider.(string), target, authCheckInterval)
|
||||
return
|
||||
}
|
||||
if claims.AuthorizationCheckInterval != authCheckInterval {
|
||||
logrus.Error("authorization check interval mismatch; restarting auth flow")
|
||||
oauthLoginRequired(w, r, shrToken, pcfg, provider.(string), target, authCheckInterval)
|
||||
return
|
||||
}
|
||||
if validDomains, found := oauthCfg.(map[string]interface{})["email_domains"]; found {
|
||||
if castedDomains, ok := validDomains.([]interface{}); !ok {
|
||||
logrus.Error("invalid email domain format")
|
||||
return
|
||||
} else {
|
||||
if len(castedDomains) > 0 {
|
||||
found := false
|
||||
for _, domain := range castedDomains {
|
||||
if strings.HasSuffix(claims.Email, domain.(string)) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
logrus.Warnf("invalid email domain")
|
||||
unauthorizedUi.WriteUnauthorized(w)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
handler.ServeHTTP(w, r)
|
||||
return
|
||||
|
||||
} else {
|
||||
logrus.Warnf("%v -> no provider for '%v'", r.RemoteAddr, provider)
|
||||
notFoundUi.WriteNotFound(w)
|
||||
}
|
||||
} else {
|
||||
logrus.Warnf("%v -> no oauth cfg for '%v'", r.RemoteAddr, shrToken)
|
||||
notFoundUi.WriteNotFound(w)
|
||||
}
|
||||
default:
|
||||
logrus.Infof("invalid auth scheme '%v'", scheme)
|
||||
writeUnauthorizedResponse(w, realm)
|
||||
basicAuthRequired(w, shrToken)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
@ -209,14 +309,53 @@ func authHandler(handler http.Handler, realm string, cfg *Config, ctx ziti.Conte
|
||||
}
|
||||
}
|
||||
|
||||
func writeUnauthorizedResponse(w http.ResponseWriter, realm string) {
|
||||
type ZrokClaims struct {
|
||||
Email string `json:"email"`
|
||||
AccessToken string `json:"accessToken"`
|
||||
Provider string `json:"provider"`
|
||||
AuthorizationCheckInterval time.Duration `json:"authorizationCheckInterval"`
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func SetZrokCookie(w http.ResponseWriter, domain, email, accessToken, provider string, checkInterval time.Duration, key []byte) {
|
||||
tkn := jwt.NewWithClaims(jwt.SigningMethodHS256, ZrokClaims{
|
||||
Email: email,
|
||||
AccessToken: accessToken,
|
||||
Provider: provider,
|
||||
AuthorizationCheckInterval: checkInterval,
|
||||
})
|
||||
sTkn, err := tkn.SignedString(key)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("after signing cookie token: %v", err.Error()), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
http.SetCookie(w, &http.Cookie{
|
||||
Name: "zrok-access",
|
||||
Value: sTkn,
|
||||
MaxAge: int(checkInterval.Seconds()),
|
||||
Domain: domain,
|
||||
Path: "/",
|
||||
Expires: time.Now().Add(checkInterval),
|
||||
//Secure: true, //When tls gets added have this be configured on if tls
|
||||
})
|
||||
}
|
||||
|
||||
func basicAuthRequired(w http.ResponseWriter, realm string) {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte("No Authorization\n"))
|
||||
_, _ = w.Write([]byte("No Authorization\n"))
|
||||
}
|
||||
|
||||
func oauthLoginRequired(w http.ResponseWriter, r *http.Request, shrToken string, pcfg *Config, provider, target string, authCheckInterval time.Duration) {
|
||||
scheme := "https"
|
||||
if pcfg.Oauth != nil && pcfg.Oauth.RedirectHttpOnly {
|
||||
scheme = "http"
|
||||
}
|
||||
http.Redirect(w, r, fmt.Sprintf("%s://%s.%s:%d/%s/login?targethost=%s&checkInterval=%s", scheme, shrToken, pcfg.Oauth.RedirectHost, pcfg.Oauth.RedirectPort, provider, url.QueryEscape(target), authCheckInterval.String()), http.StatusFound)
|
||||
}
|
||||
|
||||
func resolveService(hostMatch string, host string) string {
|
||||
logrus.Debugf("host = '%v'", host)
|
||||
if hostMatch == "" || strings.Contains(host, hostMatch) {
|
||||
tokens := strings.Split(host, ".")
|
||||
if len(tokens) > 0 {
|
||||
|
6
endpoints/publicProxy/unauthorizedUi/embed.go
Normal file
@ -0,0 +1,6 @@
|
||||
package unauthorizedUi
|
||||
|
||||
import "embed"
|
||||
|
||||
//go:embed index.html
|
||||
var FS embed.FS
|
21
endpoints/publicProxy/unauthorizedUi/handler.go
Normal file
@ -0,0 +1,21 @@
|
||||
package unauthorizedUi
|
||||
|
||||
import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func WriteUnauthorized(w http.ResponseWriter) {
|
||||
if data, err := FS.ReadFile("index.html"); err == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
n, err := w.Write(data)
|
||||
if n != len(data) {
|
||||
logrus.Errorf("short write")
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
400
endpoints/publicProxy/unauthorizedUi/index.html
Normal file
@ -0,0 +1,400 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<meta name="theme-color" content="#000000"/>
|
||||
<meta name="description" content="zrok ui"/>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Russo+One&display=swap" rel="stylesheet">
|
||||
<title>zrok</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 25;
|
||||
font-family: 'JetBrains Mono', Consolas, 'Courier New', monospace;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'Russo One', sans-serif;
|
||||
}
|
||||
table {
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
td {
|
||||
vertical-align: top;
|
||||
}
|
||||
td:last-child {
|
||||
width: 80%;
|
||||
overflow: clip;
|
||||
}
|
||||
td:first-child {
|
||||
width: 20%;
|
||||
overflow: auto;
|
||||
}
|
||||
#banner {
|
||||
background-color: #3b2693;
|
||||
text-align: center;
|
||||
padding: 25px;
|
||||
color: white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 600px;
|
||||
justify-content: center;
|
||||
}
|
||||
#banner h1 {
|
||||
font-size: 64pt;
|
||||
}
|
||||
#container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
#info {
|
||||
width: 920px;
|
||||
text-align: center;
|
||||
}
|
||||
#info h1 {
|
||||
font-size: 96pt;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
<div id="banner">
|
||||
<svg id="ziggy" viewBox="0 0 21.9 37.6" style="width: 200px; height: 343.38px;">
|
||||
<style type="text/css">
|
||||
.st0 {
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
|
||||
.st1 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: #FFFFFF;
|
||||
}
|
||||
|
||||
.st2 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: #A3A3A3;
|
||||
}
|
||||
|
||||
.st3 {
|
||||
fill: #00001F;
|
||||
}
|
||||
|
||||
.st4 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: #B3B3B3;
|
||||
}
|
||||
|
||||
.st5 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: #E6E6E6;
|
||||
}
|
||||
|
||||
.st6 {
|
||||
fill: #FFFFFF;
|
||||
stroke: #000000;
|
||||
stroke-width: 0.3;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
|
||||
.st7 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: #FF0000;
|
||||
}
|
||||
|
||||
.st8 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: #00FF00;
|
||||
}
|
||||
|
||||
.st9 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
fill: #06B806;
|
||||
}
|
||||
|
||||
.st10 {
|
||||
fill-rule: evenodd;
|
||||
clip-rule: evenodd;
|
||||
}
|
||||
|
||||
.st11 {
|
||||
fill: #B3B3B3;
|
||||
}
|
||||
|
||||
.st12 {
|
||||
opacity: 0.25;
|
||||
}
|
||||
|
||||
.st13 {
|
||||
fill: #FEFCF9;
|
||||
}
|
||||
|
||||
.st14 {
|
||||
fill: none;
|
||||
stroke: #FFFFFF;
|
||||
stroke-width: 0.4;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
stroke-miterlimit: 10;
|
||||
}
|
||||
</style>
|
||||
<rect x="5.5" y="14.9" class="st0" width="10.7" height="5.7"/>
|
||||
<polygon class="st0" points="16.9,35.4 17.3,35 17.2,34.5 17.1,34.2 16.2,26.5 6.1,26.6 5.1,34.2 4.9,34.8 5.4,35.2 7.3,35.1
|
||||
8.4,35.3 9,35.2 9.3,34.8 9.1,34.3 10.8,28.6 11.5,28.6 11.5,29.1 13,34.3 12.9,34.8 13.2,35.2 14,35.6 "/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st1" d="M16.4,16.1c0.3,0.1,2.8,0.8,3.4,1.7c0.5,0.9,2,4.8,2,5.8c0,0.1,0,2.6-0.2,3.3c-0.1,0.3-1.7,0.2-2.2,0.1,c-0.2,0,0.1-1.4,0-2.2c-0.1-1.6-0.5-2.6-0.6-2.4c-0.2,0.6-2.1,1.6-2.1,1.6"/>
|
||||
<path class="st2" d="M21.7,26.4c0,0.2,0,0.3-0.1,0.4c-0.1,0.3-1.7,0.2-2.2,0.1c-0.2,0,0.1-1.4,0-2.2c-0.1-1.6-0.5-2.6-0.6-2.4
|
||||
c-0.2,0.6-1.6,2.1-1.6,2.1l-0.3,0.1c-0.1-0.1,0.1-0.8,0.1-0.9c-0.4-0.8,1.5-2.6,1.7-2.9c0.3-0.3,0.5,0.1,0.5,0.7
|
||||
c0,0.5,0,0.6,0,0.6c0.9,3.7,0.5,4.5,0.5,4.5s0.2,0.1,0.9,0c0.7,0,0.9-0.2,0.9-0.2L21.7,26.4z"/>
|
||||
<path class="st3" d="M16.4,24.8c-1.6-2.7,2-0.6,2.2-1.8c0,0,0,0,0,0c0-0.5,0.1-1.1,0.2-1.5c0-0.2,0.1-0.4,0.1-0.4
|
||||
c0-0.1,0.1-0.1,0.1-0.1s0.1,0.1,0.1,0.1c0,0,0,0.2-0.1,0.4c0,0.2-0.1,0.4-0.1,0.6c0.1,0.1,0.2,0.3,0.2,0.4
|
||||
c0.1,0.3,0.3,0.9,0.4,1.7c0,0.2,0,0.3,0,0.5c0,0.4,0,0.9,0,1.3c0,0.4,0,0.8,0,0.8c0.3,0.1,0.8,0.1,1.4,0.1c0.3,0,0.6,0,0.6-0.1
|
||||
c0.2-0.6,0.2-2.7,0.2-3.1c0,0,0-0.1,0-0.1c0-0.4-0.3-1.4-0.6-2.4c-0.5-1.4-1.1-2.8-1.3-3.3c-0.2-0.3-0.6-0.6-1.1-0.8
|
||||
c-0.9-0.5-2-0.8-2.2-0.8 M16.5,16c0.2,0,1.3,0.4,2.3,0.8c0.5,0.3,1,0.6,1.2,0.9c0.3,0.5,0.9,2,1.4,3.4c0.4,1.1,0.6,2.1,0.6,2.5
|
||||
c0,0,0,0,0,0.1c0,0.4,0,2.6-0.2,3.2c-0.1,0.2-0.4,0.3-0.8,0.3c-0.5,0-1.2,0-1.5-0.1c-0.2,0-0.2-0.5-0.1-1.1c0-0.4,0.1-0.9,0-1.3
|
||||
c-0.1-1-0.2-1.7-0.4-2.1c0,0.1-0.8,0-0.8,0.1 M17.4,23.3l0.8-0.3c-0.1,1.2-1.5,2-1.5,2.2"/>
|
||||
<path class="st1"
|
||||
d="M21.5,27L21.5,27c-0.5,0.1-1.5,0.1-2,0h-0.1v1.3c0,0.4,0.3,0.8,0.8,0.8h0.6c0.4,0,0.8-0.3,0.8-0.8V27z"/>
|
||||
<path class="st2" d="M21.5,27L21.5,27c-0.5,0.1-1.5,0.1-2,0h-0.1v1.3c0,0.2,0.3,0.4,0.4,0.6c0-0.1,0-0.2,0-0.2v-1.2H20
|
||||
c0.4,0.1,1,0.1,1.6,0V27z"/>
|
||||
<path class="st3" d="M21.7,26.8V27v1.3c0,0.2-0.1,0.5-0.3,0.6c-0.2,0.2-0.4,0.3-0.6,0.3h-0.6c-0.2,0-0.5-0.1-0.6-0.3
|
||||
c-0.2-0.2-0.3-0.4-0.3-0.6V27v-0.2h0.2h0.1h0l0,0c0.3,0,0.7,0.1,1.1,0.1c0.3,0,0.6,0,0.8-0.1l0,0h0h0H21.7z M20.6,27.2
|
||||
c-0.4,0-0.8,0-1-0.1v1.1c0,0.2,0.1,0.3,0.2,0.4c0.1,0.1,0.3,0.2,0.4,0.2h0.6c0.2,0,0.3-0.1,0.4-0.2c0.1-0.1,0.2-0.3,0.2-0.4v-1.1
|
||||
C21.2,27.2,20.9,27.2,20.6,27.2L20.6,27.2z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st1" d="M5.5,16.2c-0.3,0.1-2.8,0.8-3.4,1.7c-0.5,0.9-2,4.8-2,5.8c0,0.1,0,2.6,0.2,3.3C0.4,27.2,2,27.2,2.5,27
|
||||
c0.2,0-0.1-1.4,0-2.2c0.1-1.6,0.5-2.6,0.6-2.4C3.3,23,5.2,24,5.2,24"/>
|
||||
<path class="st2" d="M0.3,26.5c0,0.2,0,0.3,0.1,0.4C0.4,27.2,2,27.2,2.5,27c0.2,0-0.1-1.4,0-2.2c0.1-1.6,0.5-2.6,0.6-2.4
|
||||
c0.2,0.6,1.6,2.1,1.6,2.1L5,24.7c0.1-0.1-0.1-0.8-0.1-0.9c0.4-0.8-1.5-2.6-1.7-2.9c-0.3-0.3-0.5,0.1-0.5,0.7c0,0.5,0,0.6,0,0.6
|
||||
c-0.9,3.7-0.5,4.5-0.5,4.5s-0.2,0.1-0.9,0c-0.7,0-0.9-0.2-0.9-0.2L0.3,26.5z"/>
|
||||
<path class="st3" d="M5.5,16.3c-0.2,0-1.3,0.3-2.2,0.8c-0.5,0.2-0.9,0.5-1.1,0.8c-0.3,0.5-0.9,2-1.3,3.3c-0.4,1-0.6,2-0.6,2.4
|
||||
c0,0,0,0,0,0.1c0,0.4,0,2.6,0.2,3.1c0,0.1,0.3,0.1,0.6,0.1c0.5,0,1.1,0,1.4-0.1c0,0,0-0.4,0-0.8c0-0.4-0.1-0.9,0-1.3
|
||||
c0-0.2,0-0.3,0-0.5c0.1-0.8,0.2-1.4,0.4-1.7c0-0.1,0.1-0.3,0.2-0.4c0-0.2-0.1-0.4-0.1-0.6c0-0.2-0.1-0.4-0.1-0.4
|
||||
C2.8,21.1,2.9,21,3,21c0.1,0,0.1,0.1,0.1,0.1c0,0,0,0.2,0.1,0.4c0.1,0.4,0.1,1,0.2,1.5c0,0,0,0,0,0c0.2,1.3,3.8-0.8,2.2,1.8
|
||||
M3.8,22.8c0-0.1-0.8,0-0.8-0.1c-0.1,0.4-0.3,1.1-0.4,2.1c0,0.4,0,0.9,0,1.3c0,0.6,0.1,1-0.1,1.1c-0.3,0.1-1,0.1-1.5,0.1
|
||||
c-0.4,0-0.8-0.1-0.8-0.3C0,26.4,0,24.2,0,23.8c0,0,0-0.1,0-0.1c0-0.4,0.3-1.4,0.6-2.5c0.5-1.4,1.1-2.9,1.4-3.4
|
||||
c0.2-0.3,0.6-0.7,1.2-0.9c0.9-0.5,2.1-0.8,2.3-0.8 M5.2,25.3c0-0.2-1.3-1-1.5-2.2l0.8,0.3"/>
|
||||
<path class="st1" d="M0.4,27.1L0.4,27.1c0.5,0.1,1.5,0.1,2,0h0.1v1.3c0,0.4-0.3,0.8-0.8,0.8H1.2c-0.4,0-0.8-0.3-0.8-0.8V27.1z"/>
|
||||
<path class="st2" d="M0.4,27.1L0.4,27.1c0.5,0.1,1.5,0.1,2,0h0.1v1.3c0,0.2-0.3,0.4-0.4,0.6c0-0.1,0-0.2,0-0.2v-1.2H2
|
||||
c-0.4,0.1-1,0.1-1.6,0V27.1z"/>
|
||||
<path class="st3" d="M0.4,26.9L0.4,26.9L0.4,26.9L0.4,26.9C0.6,27,1,27,1.3,27c0.4,0,0.8,0,1.1-0.1l0,0h0h0.1h0.2v0.2v1.3
|
||||
c0,0.2-0.1,0.5-0.3,0.6c-0.2,0.2-0.4,0.3-0.6,0.3H1.2c-0.2,0-0.5-0.1-0.6-0.3c-0.2-0.2-0.3-0.4-0.3-0.6v-1.3v-0.2H0.4z M1.3,27.3
|
||||
c-0.3,0-0.5,0-0.7,0v1.1c0,0.2,0.1,0.3,0.2,0.4C0.8,28.9,1,29,1.2,29h0.6c0.2,0,0.3-0.1,0.4-0.2c0.1-0.1,0.2-0.3,0.2-0.4v-1.1
|
||||
C2,27.3,1.7,27.3,1.3,27.3L1.3,27.3z"/>
|
||||
</g>
|
||||
<g>
|
||||
<path class="st2" d="M5.1,34.2h4.1l1.6-5.6c0.1,0,0.4,0,0.5,0l1.7,5.6h4.1l0-0.3c-0.1,0-0.1,0-0.2,0c-0.4,0.1-3.2,0-3.4,0
|
||||
c-0.2,0-1.6-4.9-1.6-5.1c0-0.2,0.3-0.1,0.6-0.6c0.3-0.5-0.4-0.5-0.4-0.5c-1,0.4-2,0-2,0c-0.9,0.6,0.2,1,0.2,1.1
|
||||
c0.1,0.1-1.3,5-1.5,5.1c-0.2,0.1-3.4,0-3.4,0s-0.1,0-0.2,0L5.1,34.2z"/>
|
||||
<path class="st3" d="M16.4,27.9 M6.1,27.3L6.1,27.3L6.1,27.3l-0.9,6.9h3.8l1.5-5.6c-0.4-0.1-0.5-0.3-0.5-0.3
|
||||
c-0.1-0.1-0.1-0.1,0-0.2c0.1-0.1,0.1-0.1,0.2,0c0,0,0.3,0.3,0.8,0.2c0.6,0,0.8-0.3,0.8-0.3c0.1-0.1,0.1,0,0.2,0
|
||||
c0.1,0.1,0.1,0.1,0,0.2c0,0-0.2,0.2-0.6,0.3l1.6,5.6h3.8l-0.7-7 M16.4,27.3l0.8,7.1l0,0.2h-0.2H13h-0.1l0-0.1l-1.6-5.7
|
||||
c-0.1,0-0.1,0-0.2,0c-0.1,0-0.1,0-0.2,0l-1.6,5.7l0,0.1H9.2H5.1H4.9l0-0.2l0.9-7.1"/>
|
||||
<path class="st4" d="M5.4,35.2h3.5c0.2,0,0.5-0.2,0.5-0.4l0,0c0-0.2-0.2-0.4-0.5-0.4H5.4c-0.2,0-0.5,0.2-0.5,0.4l0,0
|
||||
C4.9,35,5.1,35.2,5.4,35.2L5.4,35.2z"/>
|
||||
<path class="st3" d="M5.4,35.1h3.5C9,35.1,9,35,9.1,35c0.1-0.1,0.1-0.1,0.1-0.2c0-0.1,0-0.2-0.1-0.2C9,34.5,9,34.4,8.9,34.4H5.4
|
||||
c-0.1,0-0.2,0-0.2,0.1c-0.1,0.1-0.1,0.1-0.1,0.2c0,0.1,0,0.2,0.1,0.2C5.2,35,5.3,35.1,5.4,35.1L5.4,35.1z M8.9,35.3H5.4
|
||||
c-0.2,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.2-0.3-0.2-0.4c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.3-0.2,0.4-0.2h3.5c0.2,0,0.3,0.1,0.4,0.2
|
||||
c0.1,0.1,0.2,0.3,0.2,0.4c0,0.2-0.1,0.3-0.2,0.4C9.2,35.3,9,35.3,8.9,35.3L8.9,35.3z"/>
|
||||
<path class="st1" d="M4.8,37.5h4.4v-1.3C9.2,35.5,8.7,35,8,35H6.1c-0.7,0-1.3,0.6-1.3,1.3V37.5z"/>
|
||||
<path class="st3" d="M5,37.4h4.1v-1.1c0-0.3-0.1-0.6-0.3-0.8c-0.2-0.2-0.5-0.3-0.8-0.3H6.1c-0.3,0-0.6,0.1-0.8,0.3
|
||||
C5.1,35.6,5,35.9,5,36.2V37.4z M9.2,37.6H4.8H4.7v-0.1v-1.3c0-0.4,0.2-0.7,0.4-1c0.3-0.3,0.6-0.4,1-0.4H8c0.4,0,0.7,0.2,1,0.4
|
||||
c0.3,0.3,0.4,0.6,0.4,1v1.3v0.1H9.2z"/>
|
||||
<path class="st4" d="M13.4,35.2h3.5c0.2,0,0.5-0.2,0.5-0.4l0,0c0-0.2-0.2-0.4-0.5-0.4h-3.5c-0.2,0-0.5,0.2-0.5,0.4l0,0
|
||||
C12.9,35,13.1,35.2,13.4,35.2L13.4,35.2z"/>
|
||||
<path class="st3" d="M13.4,35.1h3.5c0.1,0,0.2,0,0.2-0.1c0.1-0.1,0.1-0.1,0.1-0.2c0-0.1,0-0.2-0.1-0.2c-0.1-0.1-0.1-0.1-0.2-0.1
|
||||
h-3.5c-0.1,0-0.2,0-0.2,0.1c-0.1,0.1-0.1,0.1-0.1,0.2c0,0.1,0,0.2,0.1,0.2C13.2,35,13.3,35.1,13.4,35.1L13.4,35.1z M16.9,35.3
|
||||
h-3.5c-0.2,0-0.3-0.1-0.4-0.2c-0.1-0.1-0.2-0.3-0.2-0.4c0-0.2,0.1-0.3,0.2-0.4c0.1-0.1,0.3-0.2,0.4-0.2h3.5c0.2,0,0.3,0.1,0.4,0.2
|
||||
c0.1,0.1,0.2,0.3,0.2,0.4c0,0.2-0.1,0.3-0.2,0.4C17.2,35.3,17,35.3,16.9,35.3L16.9,35.3z"/>
|
||||
<path class="st1" d="M13,37.5h4.4v-1.3c0-0.7-0.6-1.3-1.3-1.3h-1.8c-0.7,0-1.3,0.6-1.3,1.3V37.5z"/>
|
||||
<path class="st4" d="M17.4,37.5H13v-1.3c0-0.3,0.1-0.7,0.4-0.9c0.1,0,0.2,0,0.2,0c0,0,0,0.2,0,0.4v1.3h3.8V37.5z"/>
|
||||
<path class="st3" d="M13.1,37.4h4.1v-1.1c0-0.3-0.1-0.6-0.3-0.8c-0.2-0.2-0.5-0.3-0.8-0.3h-1.8c-0.3,0-0.6,0.1-0.8,0.3
|
||||
c-0.2,0.2-0.3,0.5-0.3,0.8V37.4z M17.4,37.6H13h-0.1v-0.1v-1.3c0-0.4,0.2-0.7,0.4-1c0.3-0.3,0.6-0.4,1-0.4h1.8
|
||||
c0.4,0,0.7,0.2,1,0.4c0.3,0.3,0.4,0.6,0.4,1v1.3v0.1H17.4z"/>
|
||||
<path class="st4" d="M9.1,37.4H5v-1.2c0-0.3,0.1-0.6,0.4-0.8c0.1,0,0.2,0,0.2,0c0,0,0,0.2,0,0.4v1.2h3.6V37.4z"/>
|
||||
</g>
|
||||
<path class="st5" d="M2.5,15.4C2.3,15.1,2,15,2.1,13.6c0.1-0.9-0.1-2.4,1-2.1l0,5.5l-1.6,2.1c0,0-0.2-2.7,0-3.2
|
||||
C1.8,15.4,2.3,15.4,2.5,15.4L2.5,15.4z"/>
|
||||
<path class="st4" d="M3.1,16.7l0,0.4l-1.6,2c0,0-0.1-1-0.1-1.9c0.3-0.3,0.7-0.5,1.1-0.7c0-0.1,0-0.2,0-0.4v-4.7
|
||||
c0.1-0.1,0.3-0.1,0.5,0L3.1,16.7L3.1,16.7z"/>
|
||||
<path class="st3" d="M2.7,15.3c0-0.1-0.1-0.1-0.1-0.1c-0.2-0.2-0.3-0.4-0.3-1.5l0,0c0-0.1,0-0.3,0-0.5c0-0.4,0-0.8,0.1-1.1
|
||||
c0.1-0.2,0.2-0.4,0.5-0.4l0,5.3l-1.2,1.6c0-0.8-0.1-2.2,0-2.6c0.1-0.2,0.2-0.3,0.3-0.3c0.1,0,0.3,0,0.4,0l0.4,0L2.7,15.3z
|
||||
M1.9,13.6c0,1,0.1,1.4,0.2,1.6c-0.1,0-0.1,0-0.2,0c-0.2,0.1-0.4,0.2-0.6,0.6c-0.2,0.5,0,3.3,0,3.3l0,0.5l0.3-0.4l1.6-2.1l0-0.1V17
|
||||
l0-5.5l0-0.1l-0.1,0c-0.7-0.2-1,0.1-1.1,0.6c-0.1,0.3-0.1,0.8-0.1,1.2C1.9,13.3,1.9,13.4,1.9,13.6L1.9,13.6z"/>
|
||||
<path class="st5" d="M6.1,10.5h-2c-0.6,0-1,0.5-1,1v5.3c0,0.6,0.5,1,1,1h2c0.6,0,1-0.5,1-1v-5.3C7.2,11,6.7,10.5,6.1,10.5L6.1,10.5
|
||||
z"/>
|
||||
<path class="st4" d="M3.1,16.5v0.7c0,0.6,0.5,1,1,1h2c0.6,0,1-0.5,1-1v-0.2c0,0,0,0,0.1,0v-5.2c0-0.6-0.5-1-1-1h-2
|
||||
c-0.6,0-1,0.5-1,1v1.8c0.5,0.8,1.1,1.4,1.8,2C4.4,15.9,3.7,16.2,3.1,16.5L3.1,16.5z"/>
|
||||
<path class="st3" d="M6.2,10.3h-2c-0.3,0-0.7,0.1-0.9,0.4C3.1,10.9,3,11.2,3,11.5v5.3c0,0.3,0.1,0.6,0.4,0.9
|
||||
C3.5,17.9,3.9,18,4.2,18h2c0.3,0,0.7-0.1,0.9-0.4c0.2-0.2,0.4-0.5,0.4-0.9v-5.3c0-0.3-0.1-0.6-0.4-0.9C6.8,10.5,6.5,10.3,6.2,10.3
|
||||
L6.2,10.3z M4.2,10.7h2c0.2,0,0.4,0.1,0.6,0.2C6.9,11.1,7,11.3,7,11.5v5.3c0,0.2-0.1,0.4-0.2,0.6c-0.2,0.1-0.4,0.2-0.6,0.2h-2
|
||||
c-0.2,0-0.4-0.1-0.6-0.2c-0.2-0.1-0.2-0.4-0.2-0.6v-5.3c0-0.2,0.1-0.4,0.2-0.6C3.7,10.8,4,10.7,4.2,10.7L4.2,10.7z"/>
|
||||
<path class="st5" d="M19.4,15.4c0.2-0.3,0.5-0.4,0.4-1.8c-0.1-0.9,0.1-2.4-1-2.1l0,5.5l1.6,2.1c0,0,0.2-2.7,0-3.2
|
||||
C20.2,15.4,19.7,15.4,19.4,15.4L19.4,15.4z"/>
|
||||
<path class="st4" d="M18.8,16.9l0,0.4l1.6,2c0,0,0.1-1,0.1-1.9c-0.3-0.3-0.7-0.5-1.1-0.7c0-0.1,0-0.2,0-0.4v-4.8
|
||||
c-0.1-0.1-0.3-0.1-0.5,0L18.8,16.9L18.8,16.9z"/>
|
||||
<path class="st3" d="M19.3,15.3c0-0.1,0.1-0.1,0.1-0.1c0.2-0.2,0.3-0.4,0.3-1.5l0,0c0-0.1,0-0.3,0-0.5c0-0.4,0-0.8-0.1-1.1
|
||||
c-0.1-0.2-0.2-0.4-0.5-0.4l0,5.3l1.2,1.6c0-0.8,0.1-2.2,0-2.6c-0.1-0.2-0.2-0.3-0.3-0.3c-0.1,0-0.3,0-0.4,0l-0.4,0L19.3,15.3z
|
||||
M20,13.6c0,1-0.1,1.4-0.2,1.6c0.1,0,0.1,0,0.2,0c0.2,0.1,0.4,0.2,0.6,0.6c0.2,0.5,0,3.3,0,3.3l0,0.5l-0.3-0.4l-1.6-2.1l0-0.1
|
||||
l0-0.1l0-5.5l0-0.1l0.1,0c0.7-0.2,1,0.1,1.1,0.6c0.1,0.3,0.1,0.8,0.1,1.2C20,13.3,20,13.4,20,13.6L20,13.6z"/>
|
||||
<path class="st5" d="M15.8,10.5h2c0.6,0,1,0.5,1,1v5.3c0,0.6-0.5,1-1,1h-2c-0.6,0-1-0.5-1-1v-5.3C14.8,11,15.2,10.5,15.8,10.5
|
||||
L15.8,10.5z"/>
|
||||
<path class="st4" d="M18.8,16.5v0.7c0,0.6-0.5,1-1,1h-2c-0.6,0-1-0.5-1-1v-2c0.6,0,1,0,1,0C15.9,15.3,17.4,15.8,18.8,16.5
|
||||
L18.8,16.5z"/>
|
||||
<path class="st3" d="M15.8,10.3h2c0.3,0,0.7,0.1,0.9,0.4c0.2,0.2,0.4,0.5,0.4,0.9v5.3c0,0.3-0.1,0.6-0.4,0.9
|
||||
c-0.2,0.2-0.5,0.4-0.9,0.4h-2c-0.3,0-0.7-0.1-0.9-0.4c-0.2-0.2-0.4-0.5-0.4-0.9v-5.3c0-0.3,0.1-0.6,0.4-0.9
|
||||
C15.1,10.5,15.4,10.3,15.8,10.3L15.8,10.3z M17.7,10.7h-2c-0.2,0-0.4,0.1-0.6,0.2c-0.2,0.1-0.2,0.4-0.2,0.6v5.3
|
||||
c0,0.2,0.1,0.4,0.2,0.6c0.2,0.1,0.4,0.2,0.6,0.2h2c0.2,0,0.4-0.1,0.6-0.2c0.2-0.1,0.2-0.4,0.2-0.6v-5.3c0-0.2-0.1-0.4-0.2-0.6
|
||||
C18.2,10.8,18,10.7,17.7,10.7L17.7,10.7z"/>
|
||||
<g>
|
||||
<ellipse transform="matrix(0.9999 -1.050124e-02 1.050124e-02 0.9999 -0.2761 0.1189)" class="st6" cx="11.2"
|
||||
cy="26.4" rx="6.3" ry="2.7"/>
|
||||
</g>
|
||||
<path class="st2" d="M14.4,16.6c-0.3-0.1-0.6-0.2-0.6-0.2c-0.2,0-5.4,0-5.7,0c-0.1,0-0.5,0.1-1.1,0.3C9.2,18,12.2,18,14.4,16.6
|
||||
L14.4,16.6z"/>
|
||||
<path class="st6" d="M3.2,18.6l0.1,6.1c0,1.8,3.6,3.3,7.9,3.2s7.7-1.6,7.8-3.4c0-0.2,0-2.9,0-6.4l-0.1-1.3c0,1.8-3.3,0.9-7.6,0.9
|
||||
s-8.1,0.9-8.1-0.9L3.2,18.6z"/>
|
||||
<g>
|
||||
<g>
|
||||
<path class="st2" d="M15.7,16c-0.7-0.3-1.2-0.5-1.3-0.5c-0.9,1.3-0.7,7.7-0.8,7.9c-0.2,0.2-0.2,0.7-0.1,1.3
|
||||
c0.1,0.6,1.5,1.1,1.8,0c0.4-1.1-0.4-1.4-0.4-1.4C14.6,20.8,15.3,16.8,15.7,16L15.7,16z"/>
|
||||
<path class="st2" d="M7.5,15.6c-0.3,0.1-0.8,0.2-1.3,0.5c0.4,0.5,1.1,4.8,0.9,7.3c0,0-0.7,0.3-0.4,1.4c0.4,1.1,1.7,0.6,1.8,0
|
||||
c0.1-0.6,0-1.1-0.1-1.3C8.2,23.4,8.5,16.8,7.5,15.6L7.5,15.6z"/>
|
||||
<path class="st2" d="M7.6,15.6c-0.3,0.1-0.8,0.2-1.3,0.5c0.4,0.6,1.1,4.8,0.9,7.3c0,0-0.7,0.3-0.4,1.4c0.4,1.1,1.7,0.6,1.8,0
|
||||
c0.1-0.6,0-1.1-0.1-1.3C8.2,23.3,8.5,16.8,7.6,15.6L7.6,15.6z"/>
|
||||
<path class="st2" d="M9.4,23.6h3.2c0.5,0,0.8,0.4,0.8,0.9v1.4c0,0.5-0.4,0.9-0.8,0.9H9.4c-0.5,0-0.8-0.4-0.8-0.9v-1.4
|
||||
C8.5,24,8.9,23.6,9.4,23.6L9.4,23.6z"/>
|
||||
<path class="st1" d="M9.6,23.9h2.7c0.4,0,0.7,0.3,0.7,0.7v1.1c0,0.4-0.3,0.7-0.7,0.7H9.6c-0.4,0-0.7-0.3-0.7-0.7v-1.1
|
||||
C8.9,24.3,9.2,23.9,9.6,23.9L9.6,23.9z"/>
|
||||
<path class="st2" d="M11.5,23.9h0.3c0.5,0.1,0.9,0.5,0.9,1c0,0.6-0.5,1-1,1c-0.6,0-1-0.5-1-1C10.6,24.4,11,24,11.5,23.9
|
||||
L11.5,23.9z"/>
|
||||
<path class="st2" d="M10,24.2c0.4,0,0.8,0.3,0.8,0.8c0,0.4-0.3,0.8-0.8,0.8c-0.4,0-0.8-0.3-0.8-0.8C9.2,24.6,9.5,24.2,10,24.2
|
||||
L10,24.2z"/>
|
||||
<path class="st7" d="M11.6,24.3c0.4,0,0.7,0.3,0.7,0.7c0,0.4-0.3,0.7-0.7,0.7c-0.4,0-0.7-0.3-0.7-0.7
|
||||
C10.9,24.6,11.2,24.3,11.6,24.3L11.6,24.3z"/>
|
||||
<path class="st3" d="M11.6,24.1c0.2,0,0.4,0.1,0.6,0.2c0.1,0.1,0.2,0.4,0.2,0.6c0,0.2-0.1,0.4-0.2,0.6c-0.1,0.1-0.4,0.2-0.6,0.2
|
||||
c-0.2,0-0.4-0.1-0.6-0.2c-0.1-0.1-0.2-0.4-0.2-0.6c0-0.2,0.1-0.4,0.2-0.6C11.1,24.2,11.3,24.1,11.6,24.1L11.6,24.1z M11.9,24.6
|
||||
c-0.1-0.1-0.2-0.2-0.4-0.2c-0.1,0-0.3,0.1-0.4,0.2c-0.1,0.1-0.2,0.2-0.2,0.4c0,0.1,0.1,0.3,0.2,0.4c0.1,0.1,0.2,0.2,0.4,0.2
|
||||
c0.1,0,0.3-0.1,0.4-0.2c0.1-0.1,0.2-0.2,0.2-0.4C12.1,24.8,12,24.7,11.9,24.6L11.9,24.6z"/>
|
||||
<path class="st8" d="M10,24.6c0.2,0,0.4,0.2,0.4,0.4c0,0.2-0.2,0.4-0.4,0.4c-0.2,0-0.4-0.2-0.4-0.4C9.5,24.7,9.7,24.6,10,24.6
|
||||
L10,24.6z"/>
|
||||
<path class="st9" d="M10.3,24.7c0.1,0.1,0.1,0.2,0.1,0.3c0,0.2-0.2,0.4-0.4,0.4c-0.2-0.1-0.3-0.2-0.2-0.2
|
||||
C10,25.2,10.2,25,10.3,24.7C10.2,24.8,10.2,24.7,10.3,24.7L10.3,24.7z"/>
|
||||
<path class="st3" d="M10,24.4c0.2,0,0.3,0.1,0.4,0.2c0.1,0.1,0.2,0.2,0.2,0.4c0,0.2-0.1,0.3-0.2,0.4c-0.1,0.1-0.2,0.2-0.4,0.2
|
||||
c-0.2,0-0.3-0.1-0.4-0.2S9.4,25.1,9.4,25c0-0.2,0.1-0.3,0.2-0.4C9.7,24.5,9.8,24.4,10,24.4L10,24.4z M10.2,24.8
|
||||
c0,0-0.1-0.1-0.2-0.1c-0.1,0-0.1,0-0.2,0.1c0,0-0.1,0.1-0.1,0.2c0,0.1,0,0.1,0.1,0.2c0,0,0.1,0.1,0.2,0.1c0.1,0,0.1,0,0.2-0.1
|
||||
c0,0,0.1-0.1,0.1-0.2C10.2,24.9,10.2,24.8,10.2,24.8L10.2,24.8z"/>
|
||||
<path class="st3" d="M9.6,23.8h2.7c0.2,0,0.5,0.1,0.6,0.3c0.2,0.2,0.3,0.4,0.3,0.6v1.1c0,0.2-0.1,0.5-0.3,0.6
|
||||
c-0.2,0.2-0.4,0.3-0.6,0.3H9.6c-0.2,0-0.5-0.1-0.6-0.3c-0.2-0.2-0.3-0.4-0.3-0.6v-1.1c0-0.2,0.1-0.5,0.3-0.6
|
||||
C9.1,23.9,9.4,23.8,9.6,23.8L9.6,23.8z M12.3,24.1H9.6c-0.2,0-0.3,0.1-0.4,0.2C9.1,24.4,9,24.5,9,24.7v1.1c0,0.2,0.1,0.3,0.2,0.4
|
||||
c0.1,0.1,0.2,0.2,0.4,0.2h2.7c0.2,0,0.3-0.1,0.4-0.2c0.1-0.1,0.2-0.2,0.2-0.4v-1.1c0-0.2-0.1-0.3-0.2-0.4
|
||||
C12.7,24.2,12.5,24.1,12.3,24.1L12.3,24.1z"/>
|
||||
<path class="st3" d="M7.5,23.6h0.1C8,20.3,7.3,16.8,7.2,16l-0.3,0.1c0.6,1.5,0.8,4.3,0.8,6C7.6,22.6,7.6,23.2,7.5,23.6L7.5,23.6z
|
||||
M7.9,23.7c0,0,0.1,0,0.1,0.1c0.1,0.1,0.1,0.2,0.1,0.3v0.4c0,0.1-0.1,0.2-0.1,0.3c-0.1,0.1-0.2,0.1-0.3,0.1H7.3
|
||||
c-0.1,0-0.2-0.1-0.3-0.1c-0.1-0.1-0.1-0.2-0.1-0.3v-0.4c0-0.1,0.1-0.2,0.1-0.3c0.1-0.1,0.1-0.1,0.2-0.1c0-0.1,0-0.1,0-0.3
|
||||
c0-0.3,0-0.7,0-1.1c0-1.8-0.1-4.6-0.8-6L6.4,16l0.2-0.1l0.6-0.2l0.2-0.1l0,0.2C7.4,15.8,8.3,19.8,7.9,23.7L7.9,23.7z M7.8,23.9
|
||||
L7.8,23.9l-0.4,0c0,0-0.1,0-0.1,0c0,0,0,0.1,0,0.1v0.4c0,0,0,0.1,0,0.1c0,0,0.1,0,0.1,0h0.4c0,0,0.1,0,0.1,0c0,0,0-0.1,0-0.1
|
||||
v-0.4C7.9,24.1,7.9,24,7.8,23.9C7.8,24,7.8,24,7.8,23.9L7.8,23.9z"/>
|
||||
<path class="st3" d="M7.6,23.6h0.1C8,20.2,7.4,16.8,7.2,16l-0.3,0.1c0.6,1.5,0.8,4.3,0.8,6C7.6,22.6,7.6,23.2,7.6,23.6L7.6,23.6z
|
||||
M8,23.7c0,0,0.1,0,0.1,0.1c0.1,0.1,0.1,0.2,0.1,0.3v0.4c0,0.1-0.1,0.2-0.1,0.3c-0.1,0.1-0.2,0.1-0.3,0.1H7.4
|
||||
c-0.1,0-0.2-0.1-0.3-0.1c-0.1-0.1-0.1-0.2-0.1-0.3v-0.4c0-0.1,0.1-0.2,0.1-0.3c0.1-0.1,0.1-0.1,0.2-0.1c0-0.1,0-0.1,0-0.3
|
||||
c0-0.3,0-0.7,0-1.1c0-1.8-0.1-4.6-0.8-6l-0.1-0.1l0.2-0.1l0.6-0.2l0.2-0.1l0,0.2C7.5,15.8,8.4,19.8,8,23.7L8,23.7z M7.8,23.9
|
||||
L7.8,23.9l-0.4,0c0,0-0.1,0-0.1,0c0,0,0,0.1,0,0.1v0.4c0,0,0,0.1,0,0.1c0,0,0.1,0,0.1,0h0.4c0,0,0.1,0,0.1,0c0,0,0-0.1,0-0.1
|
||||
v-0.4C7.9,24,7.9,24,7.8,23.9C7.9,23.9,7.8,23.9,7.8,23.9L7.8,23.9z"/>
|
||||
<path class="st3" d="M14.4,23.5h-0.1c-0.3-3.4,0.3-6.8,0.5-7.6L15,16c-0.6,1.5-0.8,4.3-0.8,6C14.3,22.4,14.3,23,14.4,23.5
|
||||
L14.4,23.5z M13.9,23.5c0,0-0.1,0-0.1,0.1c-0.1,0.1-0.1,0.2-0.1,0.3v0.4c0,0.1,0.1,0.2,0.1,0.3c0.1,0.1,0.2,0.1,0.3,0.1h0.4
|
||||
c0.1,0,0.2-0.1,0.3-0.1c0.1-0.1,0.1-0.2,0.1-0.3v-0.4c0-0.1-0.1-0.2-0.1-0.3c-0.1-0.1-0.1-0.1-0.2-0.1c0-0.1,0-0.1,0-0.3
|
||||
c0-0.3,0-0.7,0-1.1c0-1.8,0.1-4.6,0.8-6l0.1-0.1l-0.2-0.1l-0.6-0.2l-0.2-0.1l0,0.2C14.5,15.6,13.5,19.7,13.9,23.5L13.9,23.5z
|
||||
M14.1,23.8L14.1,23.8l0.4,0c0,0,0.1,0,0.1,0c0,0,0,0.1,0,0.1v0.4c0,0,0,0.1,0,0.1c0,0-0.1,0-0.1,0h-0.4c0,0-0.1,0-0.1,0
|
||||
c0,0,0-0.1,0-0.1v-0.4C14,23.9,14,23.8,14.1,23.8C14.1,23.8,14.1,23.8,14.1,23.8L14.1,23.8z"/>
|
||||
</g>
|
||||
</g>
|
||||
<path class="st4" d="M15.5,10.6h2c0.6,0,1,0.5,1,1v1.8c-0.6,0.9-1.3,1.7-2.2,2.4c-0.6,0.4,0,0-0.6,0.2l-0.3-4.3
|
||||
C15.4,11.2,15,10.6,15.5,10.6L15.5,10.6z"/>
|
||||
<path class="st10" d="M5.4,2.1c3.7-2.8,9.1-2.1,12,1.5c2.9,3.6,2.2,8.9-1.6,11.7c-3.7,2.8-9.1,2.1-12-1.5C1,10.2,1.7,4.9,5.4,2.1
|
||||
L5.4,2.1z"/>
|
||||
<path class="st1" d="M1.3,10.6l0.9,0C2,9.4,2,8.3,2.3,7.2l0,0l-1,0c-0.6,0-1,0.5-1,1l0,1.4C0.3,10.2,0.8,10.6,1.3,10.6L1.3,10.6z"/>
|
||||
<path class="st1" d="M19.9,10.5l-0.9,0c0.2-1.1,0.2-2.3,0-3.4l0,0l1,0c0.6,0,1,0.5,1,1l0,1.4C20.9,10.1,20.4,10.6,19.9,10.5
|
||||
L19.9,10.5z"/>
|
||||
<path d="M5.3,2c1.9-1.4,4.3-2,6.5-1.7c2.2,0.3,4.4,1.4,5.8,3.3c1.5,1.9,2,4.2,1.7,6.3c-0.3,2.2-1.4,4.2-3.4,5.7
|
||||
c-1.9,1.4-4.3,2-6.5,1.7c-2.2-0.3-4.4-1.4-5.8-3.3c-1.5-1.9-2-4.2-1.7-6.3C2.2,5.5,3.3,3.4,5.3,2L5.3,2z M11.7,0.7
|
||||
C9.6,0.5,7.4,1,5.6,2.3C3.7,3.7,2.7,5.6,2.4,7.7c-0.3,2.1,0.3,4.2,1.7,6c1.4,1.8,3.4,2.8,5.5,3.1c2.1,0.3,4.3-0.2,6.1-1.6
|
||||
c1.8-1.4,2.9-3.3,3.2-5.4c0.3-2.1-0.3-4.2-1.7-6C15.8,2,13.8,1,11.7,0.7L11.7,0.7z"/>
|
||||
<path class="st2" d="M1.3,10.6l0.9,0C2,9.4,2,8.3,2.3,7.2l0,0l-0.8,0C1.4,8.1,1.2,8.9,1.3,9.9l-0.9,0c0,0-0.1,0-0.1,0.1
|
||||
C0.5,10.3,0.9,10.6,1.3,10.6L1.3,10.6z"/>
|
||||
<path class="st2" d="M19.9,10.5l-0.9,0c0.2-1.1,0.2-2.3,0-3.4l0,0l0.8,0C19.8,8,20,8.9,19.8,9.9l0.9,0c0,0,0.1,0,0.1,0.1
|
||||
C20.7,10.3,20.3,10.5,19.9,10.5L19.9,10.5z"/>
|
||||
<path class="st3" d="M1.3,10.8l0.9,0l0.3,0l-0.1-0.3C2.3,10,2.3,9.4,2.3,8.9c0-0.5,0.1-1.1,0.2-1.6l0,0l0,0l0,0l0-0.2l-0.2,0l-1,0
|
||||
l0,0C0.9,7,0.6,7.1,0.3,7.3C0.1,7.6,0,7.9,0,8.2v0l0,1.4c0,0.3,0.2,0.6,0.4,0.9C0.7,10.7,1,10.8,1.3,10.8L1.3,10.8z M1.9,10.3
|
||||
l-0.6,0c-0.2,0-0.4-0.1-0.6-0.2C0.6,10,0.5,9.8,0.5,9.6l0-1.4l0,0c0-0.2,0.1-0.4,0.2-0.5C0.8,7.5,1,7.4,1.2,7.4h0l0.7,0
|
||||
c-0.1,0.5-0.1,1-0.2,1.5C1.8,9.4,1.9,9.8,1.9,10.3L1.9,10.3z"/>
|
||||
<path class="st3" d="M19.9,10.8l-0.9,0l-0.3,0l0.1-0.3c0.1-0.5,0.2-1.1,0.1-1.6c0-0.5-0.1-1.1-0.2-1.6l0,0l0,0l0,0l0-0.2l0.2,0l1,0
|
||||
h0c0.3,0,0.7,0.2,0.9,0.4c0.2,0.2,0.4,0.5,0.3,0.9v0l0,1.4c0,0.3-0.2,0.6-0.4,0.9C20.5,10.6,20.2,10.8,19.9,10.8L19.9,10.8z
|
||||
M19.3,10.3l0.6,0c0.2,0,0.4-0.1,0.6-0.2c0.1-0.1,0.2-0.3,0.2-0.5l0-1.4l0,0c0-0.2-0.1-0.4-0.2-0.5c-0.1-0.1-0.3-0.2-0.5-0.2l0,0
|
||||
l-0.7,0c0.1,0.5,0.1,1,0.2,1.5C19.4,9.3,19.3,9.8,19.3,10.3L19.3,10.3z"/>
|
||||
<path class="st1" d="M19.1,9.8c-0.3,2.1-1.4,4.2-3.3,5.6c-3.7,2.8-9.1,2.1-12-1.5c-0.6-0.8-1.1-1.7-1.4-2.6
|
||||
C5.7,16.6,14.9,18.9,19.1,9.8L19.1,9.8z"/>
|
||||
<path class="st3" d="M19.4,9.8c-0.1,1.1-0.5,2.2-1.1,3.2c-0.6,1-1.3,1.8-2.3,2.5c-1.9,1.4-4.3,2-6.5,1.7c-2.2-0.3-4.4-1.4-5.8-3.3
|
||||
c-0.3-0.4-0.6-0.8-0.8-1.3c-0.2-0.5-0.4-0.9-0.6-1.4L1.9,9.8l0.7,1.3c1,1.7,2.6,3.1,4.5,3.9c1.2,0.5,2.4,0.8,3.7,0.8
|
||||
c1.3,0,2.5-0.3,3.7-1c1.7-0.9,3.2-2.6,4.3-5.1l0.5-1.4L19.4,9.8z M17.8,12.8c0.2-0.4,0.4-0.8,0.6-1.2c-1,1.7-2.3,2.8-3.6,3.6
|
||||
c-1.3,0.7-2.6,1-4,1.1c-1.3,0-2.7-0.3-3.9-0.8c-1.2-0.5-2.4-1.3-3.3-2.3c0.1,0.2,0.3,0.4,0.4,0.6c1.4,1.8,3.4,2.8,5.5,3.1
|
||||
c2.1,0.3,4.3-0.2,6.1-1.6C16.6,14.5,17.3,13.7,17.8,12.8L17.8,12.8z"/>
|
||||
<path class="st1" d="M5.4,2c3.7-2.8,9.1-2.1,12,1.5c1.4,1.8,2,4,1.7,6.1c-0.8-3.3-2.3-5.1-4.3-6.1c-3.1-1.6-6.3-1.8-9.6,1
|
||||
c-2.4,2-2.7,4.5-2.8,6.6C1.4,7.8,2.5,4.1,5.4,2L5.4,2z"/>
|
||||
<path class="st3" d="M5.3,1.8c1.9-1.4,4.3-2,6.5-1.7c2.2,0.3,4.4,1.4,5.8,3.3c0.7,0.9,1.2,1.9,1.5,3c0.3,1.1,0.4,2.2,0.2,3.3
|
||||
l-0.2,1.3l-0.3-1.3c-0.4-1.6-0.9-2.9-1.6-3.9c-0.7-1-1.6-1.6-2.5-2.1c-1.5-0.8-3-1.2-4.6-1.1C8.6,2.6,7,3.2,5.4,4.6
|
||||
c-1.2,1-1.8,2.1-2.2,3.2C2.9,8.9,2.8,10,2.7,11l-0.1,1.2l-0.4-1.2c-0.5-1.7-0.5-3.5,0-5.1C2.8,4.3,3.8,2.9,5.3,1.8L5.3,1.8z
|
||||
M11.7,0.5C9.6,0.3,7.4,0.8,5.6,2.1c-1.4,1-2.4,2.4-2.9,3.9C2.3,7.2,2.2,8.3,2.4,9.5c0.1-0.6,0.2-1.2,0.4-1.9
|
||||
c0.4-1.2,1.1-2.3,2.3-3.4c1.7-1.4,3.3-2.1,5-2.2c1.6-0.1,3.3,0.4,4.8,1.2c1,0.5,1.9,1.3,2.7,2.3C18.1,6.2,18.6,7,18.9,8
|
||||
c0-0.5-0.1-1.1-0.3-1.6c-0.3-1-0.8-1.9-1.4-2.8C15.8,1.9,13.8,0.8,11.7,0.5L11.7,0.5z"/>
|
||||
<path class="st5" d="M19.2,8.8c0,0.2,0,0.5-0.1,0.7c-0.8-3.3-2.3-5.1-4.3-6.1C11.7,1.8,7.4,1.9,4.7,4.6C3.9,5.4,2.5,7.2,2.4,9.7
|
||||
C2,7.2,3,4.9,5.4,3c3.7-2.8,9.1-2.1,12,1.5C18.5,5.8,19,7.3,19.2,8.8L19.2,8.8z"/>
|
||||
<path class="st3" d="M19.4,8.8c0,0.1,0,0.3,0,0.4c0,0.1,0,0.2,0,0.4l-0.1,1.1l-0.2-1.1C18.5,8,18,6.7,17.3,5.7
|
||||
c-0.7-1-1.6-1.6-2.5-2.1c-1.6-0.8-3.5-1.2-5.3-1C7.7,2.8,6.1,3.5,4.9,4.7C4.4,5.2,3.7,6,3.2,7.1C2.9,7.8,2.6,8.7,2.6,9.7l-0.1,2.1
|
||||
l-0.3-2C2,8.4,2.1,7.2,2.7,6c0.5-1.1,1.4-2.2,2.6-3.1l0,0l0,0c1.9-1.4,4.2-2,6.5-1.7c2.2,0.3,4.3,1.4,5.8,3.2l0,0l0,0
|
||||
c0.5,0.6,0.9,1.3,1.2,2.1C19.1,7.3,19.3,8,19.4,8.8L19.4,8.8L19.4,8.8z M17.6,5.5c0.5,0.7,0.9,1.4,1.2,2.4
|
||||
c-0.1-0.4-0.2-0.8-0.4-1.2c-0.3-0.7-0.7-1.4-1.2-2l0,0c-1.4-1.8-3.4-2.8-5.5-3.1C9.6,1.3,7.4,1.8,5.6,3.2l0,0c-1.2,0.9-2,1.9-2.5,3
|
||||
c-0.2,0.5-0.4,1-0.5,1.5c0.1-0.2,0.2-0.5,0.3-0.7c0.5-1.1,1.3-2,1.8-2.5c1.3-1.3,3-2,4.8-2.2c1.9-0.2,3.9,0.2,5.5,1
|
||||
C16,3.8,16.9,4.5,17.6,5.5L17.6,5.5z"/>
|
||||
<path class="st5" d="M3.8,8.2c0,0,1.4-4.1,6.2-4.5c0.5-0.1,1.1,1.3,0.4,1.4c-0.7,0.2-3.6,0.6-5.4,4C4.7,9.4,3.5,8.9,3.8,8.2
|
||||
L3.8,8.2z"/>
|
||||
<path class="st5" d="M12.4,5.1c0.4,0,0.7-0.3,0.7-0.7c0-0.4-0.3-0.7-0.7-0.7c-0.4,0-0.7,0.3-0.7,0.7C11.7,4.8,12,5.1,12.4,5.1
|
||||
L12.4,5.1z"/>
|
||||
<path class="st11" d="M10.9,18.4l-1.5,2.3l0.9,0.6l0.1-0.2l0.5,2.2l1.5-2.3l-0.8-0.5l-0.1,0.2L10.9,18.4z M11.6,20.7l0.4,0.2
|
||||
l-1,1.5l-0.5-2.2l-0.4,0.6l-0.4-0.2l1-1.5l0.4,2.1L11.6,20.7z"/>
|
||||
<g class="st12">
|
||||
<circle class="st13" cx="14.1" cy="8.9" r="1.8"/>
|
||||
<circle cx="14.4" cy="8.7" r="0.7"/>
|
||||
<circle class="st13" cx="7.1" cy="8.9" r="1.8"/>
|
||||
<path id="XMLID_00000028285902548687721290000002641907447975500945_" class="st14" d="M10.8,14.1c-1.6,0-2.7-0.7-3.3-1.1
|
||||
c-0.1-0.1-0.1-0.2,0-0.3c0.1-0.1,0.2-0.1,0.3,0c0.5,0.4,1.7,1.1,3.3,1c1.4,0,2.4-0.7,2.9-1c0.1-0.1,0.2,0,0.3,0
|
||||
c0.1,0.1,0,0.2,0,0.3c-0.5,0.4-1.6,1-3.1,1.1C10.9,14.1,10.9,14.1,10.8,14.1z"/>
|
||||
<circle cx="7.6" cy="8.6" r="0.7"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<h1>zrok</h1>
|
||||
</div>
|
||||
<div id="container">
|
||||
<div id="info">
|
||||
<h1>Unauthorized</h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
20
etc/frontend.yml
Normal file
@ -0,0 +1,20 @@
|
||||
# Setting the `host_match` setting will cause a `zrok access public` to ignore `Host` headers that do not contain the
|
||||
# configured string. This will allow you to let a load balancer access the frontend by IP address for health check
|
||||
# purposes, and will allow `Host` headers that match the configured DNS name to be routed through `zrok`.
|
||||
#
|
||||
#host_match: zrok.io
|
||||
|
||||
# The OAuth configuration is used when enabling OAuth authentication with your public frontend.
|
||||
#
|
||||
#oauth:
|
||||
# redirect_host: oauth.zrok.io
|
||||
# redirect_port: 28080
|
||||
# redirect_http_only: false
|
||||
# hash_key: "<yourRandomHashKey>"
|
||||
# providers:
|
||||
# - name: google
|
||||
# client_id: <client-id>
|
||||
# client_secret: <client-secret>
|
||||
# - name: github
|
||||
# client_id: <client-id>
|
||||
# client_secret: <client-secret>
|
@ -1,5 +0,0 @@
|
||||
# Setting the `host_match` setting will cause a `zrok access public` to ignore `Host` headers that do not contain the
|
||||
# configured string. This will allow you to let a load balancer access the frontend by IP address for health check
|
||||
# purposes, and will allow `Host` headers that match the configured DNS name to be routed through `zrok`.
|
||||
#
|
||||
host_match: zrok.io
|
12
go.mod
@ -14,6 +14,8 @@ require (
|
||||
github.com/go-openapi/strfmt v0.21.7
|
||||
github.com/go-openapi/swag v0.22.4
|
||||
github.com/go-openapi/validate v0.22.1
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
github.com/iancoleman/strcase v0.2.0
|
||||
github.com/influxdata/influxdb-client-go/v2 v2.11.0
|
||||
@ -41,14 +43,18 @@ require (
|
||||
github.com/spf13/cobra v1.7.0
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/wneessen/go-mail v0.2.7
|
||||
github.com/zitadel/oidc/v2 v2.7.0
|
||||
go.uber.org/zap v1.25.0
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/net v0.14.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
golang.org/x/time v0.3.0
|
||||
nhooyr.io/websocket v1.8.7
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go/compute v1.20.1 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
filippo.io/edwards25519 v1.0.0 // indirect
|
||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
@ -98,7 +104,6 @@ require (
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-sql-driver/mysql v1.7.0 // indirect
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
|
||||
github.com/golang/glog v1.1.0 // indirect
|
||||
github.com/golang/mock v1.6.0 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
@ -108,8 +113,9 @@ require (
|
||||
github.com/google/go-tpm v0.3.3 // indirect
|
||||
github.com/google/go-tspi v0.3.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect
|
||||
github.com/google/uuid v1.3.0 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/schema v1.2.0 // indirect
|
||||
github.com/gorilla/securecookie v1.1.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.3 // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
@ -150,6 +156,7 @@ require (
|
||||
github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.13.0 // indirect
|
||||
github.com/muhlemmer/gu v0.3.1 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
@ -221,6 +228,7 @@ require (
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
golang.org/x/tools v0.10.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230706204954-ccb25ca9f130 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect
|
||||
|
13
go.sum
@ -39,7 +39,9 @@ cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUM
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/compute v1.20.1 h1:6aKEtlUiwEpJzM001l0yFkpXmUVXaN8W+fbkb2AZNbg=
|
||||
cloud.google.com/go/compute v1.20.1/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
@ -640,6 +642,10 @@ github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2z
|
||||
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/schema v1.2.0 h1:YufUaxZYCKGFuAq3c96BOhjgd5nmXiOY9NGzF247Tsc=
|
||||
github.com/gorilla/schema v1.2.0/go.mod h1:kgLaKoK1FELgZqMAVxx/5cbj0kT+57qxUrAlIO2eleU=
|
||||
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
|
||||
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
@ -766,6 +772,7 @@ github.com/jarcoal/httpmock v1.0.5/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jedib0t/go-pretty/v6 v6.4.3 h1:2n9BZ0YQiXGESUSR+6FLg0WWWE80u+mIz35f0uHWcIE=
|
||||
github.com/jedib0t/go-pretty/v6 v6.4.3/go.mod h1:MgmISkTWDSFu0xOqiZ0mKNntMQ2mDgOcwOkwBEkMDJI=
|
||||
github.com/jeremija/gosubmit v0.2.7 h1:At0OhGCFGPXyjPYAsCchoBUhE099pcBXmsb4iZqROIc=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
|
||||
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
|
||||
@ -985,6 +992,8 @@ github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ
|
||||
github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs=
|
||||
github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
github.com/muhlemmer/gu v0.3.1 h1:7EAqmFrW7n3hETvuAdmFmn4hS8W+z3LgKtrnow+YzNM=
|
||||
github.com/muhlemmer/gu v0.3.1/go.mod h1:YHtHR+gxM+bKEIIs7Hmi9sPT3ZDUvTN/i88wQpZkrdM=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo=
|
||||
@ -1166,6 +1175,7 @@ github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6po
|
||||
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM=
|
||||
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
@ -1351,6 +1361,8 @@ github.com/zeebo/blake3 v0.2.3/go.mod h1:mjJjZpnsyIVtVgTOSpJ9vmRE4wgDeyt2HU3qXvv
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
||||
github.com/zitadel/oidc/v2 v2.7.0 h1:IGX4EDk6tegTjUSsZDWeTfLseFU0BdJ/Glf1tgys2lU=
|
||||
github.com/zitadel/oidc/v2 v2.7.0/go.mod h1:zkUkVJS0sDVy9m0UA9RgO3f8i/C0rtjvXU36UJj7T+0=
|
||||
github.com/ziutek/mymysql v1.5.4 h1:GB0qdRGsTwQSBVYuVShFBKaXSnSnYYC2d9knnE1LHFs=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
@ -1635,6 +1647,7 @@ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.10.0 h1:zHCpF2Khkwy4mMB4bv0U37YtJdTGW8jI0glAApi0Kh8=
|
||||
golang.org/x/oauth2 v0.10.0/go.mod h1:kTpgurOux7LqtuxjuyZa4Gj2gdezIt/jQtGnNFfypQI=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
184
rest_client_zrok/share/oauth_authenticate_parameters.go
Normal file
@ -0,0 +1,184 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package share
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
cr "github.com/go-openapi/runtime/client"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// NewOauthAuthenticateParams creates a new OauthAuthenticateParams object,
|
||||
// with the default timeout for this client.
|
||||
//
|
||||
// Default values are not hydrated, since defaults are normally applied by the API server side.
|
||||
//
|
||||
// To enforce default values in parameter, use SetDefaults or WithDefaults.
|
||||
func NewOauthAuthenticateParams() *OauthAuthenticateParams {
|
||||
return &OauthAuthenticateParams{
|
||||
timeout: cr.DefaultTimeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateParamsWithTimeout creates a new OauthAuthenticateParams object
|
||||
// with the ability to set a timeout on a request.
|
||||
func NewOauthAuthenticateParamsWithTimeout(timeout time.Duration) *OauthAuthenticateParams {
|
||||
return &OauthAuthenticateParams{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateParamsWithContext creates a new OauthAuthenticateParams object
|
||||
// with the ability to set a context for a request.
|
||||
func NewOauthAuthenticateParamsWithContext(ctx context.Context) *OauthAuthenticateParams {
|
||||
return &OauthAuthenticateParams{
|
||||
Context: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateParamsWithHTTPClient creates a new OauthAuthenticateParams object
|
||||
// with the ability to set a custom HTTPClient for a request.
|
||||
func NewOauthAuthenticateParamsWithHTTPClient(client *http.Client) *OauthAuthenticateParams {
|
||||
return &OauthAuthenticateParams{
|
||||
HTTPClient: client,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
OauthAuthenticateParams contains all the parameters to send to the API endpoint
|
||||
|
||||
for the oauth authenticate operation.
|
||||
|
||||
Typically these are written to a http.Request.
|
||||
*/
|
||||
type OauthAuthenticateParams struct {
|
||||
|
||||
// Code.
|
||||
Code string
|
||||
|
||||
// State.
|
||||
State *string
|
||||
|
||||
timeout time.Duration
|
||||
Context context.Context
|
||||
HTTPClient *http.Client
|
||||
}
|
||||
|
||||
// WithDefaults hydrates default values in the oauth authenticate params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *OauthAuthenticateParams) WithDefaults() *OauthAuthenticateParams {
|
||||
o.SetDefaults()
|
||||
return o
|
||||
}
|
||||
|
||||
// SetDefaults hydrates default values in the oauth authenticate params (not the query body).
|
||||
//
|
||||
// All values with no default are reset to their zero value.
|
||||
func (o *OauthAuthenticateParams) SetDefaults() {
|
||||
// no default values defined for this parameter
|
||||
}
|
||||
|
||||
// WithTimeout adds the timeout to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) WithTimeout(timeout time.Duration) *OauthAuthenticateParams {
|
||||
o.SetTimeout(timeout)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetTimeout adds the timeout to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) SetTimeout(timeout time.Duration) {
|
||||
o.timeout = timeout
|
||||
}
|
||||
|
||||
// WithContext adds the context to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) WithContext(ctx context.Context) *OauthAuthenticateParams {
|
||||
o.SetContext(ctx)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetContext adds the context to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) SetContext(ctx context.Context) {
|
||||
o.Context = ctx
|
||||
}
|
||||
|
||||
// WithHTTPClient adds the HTTPClient to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) WithHTTPClient(client *http.Client) *OauthAuthenticateParams {
|
||||
o.SetHTTPClient(client)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetHTTPClient adds the HTTPClient to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) SetHTTPClient(client *http.Client) {
|
||||
o.HTTPClient = client
|
||||
}
|
||||
|
||||
// WithCode adds the code to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) WithCode(code string) *OauthAuthenticateParams {
|
||||
o.SetCode(code)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetCode adds the code to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) SetCode(code string) {
|
||||
o.Code = code
|
||||
}
|
||||
|
||||
// WithState adds the state to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) WithState(state *string) *OauthAuthenticateParams {
|
||||
o.SetState(state)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetState adds the state to the oauth authenticate params
|
||||
func (o *OauthAuthenticateParams) SetState(state *string) {
|
||||
o.State = state
|
||||
}
|
||||
|
||||
// WriteToRequest writes these params to a swagger request
|
||||
func (o *OauthAuthenticateParams) WriteToRequest(r runtime.ClientRequest, reg strfmt.Registry) error {
|
||||
|
||||
if err := r.SetTimeout(o.timeout); err != nil {
|
||||
return err
|
||||
}
|
||||
var res []error
|
||||
|
||||
// query param code
|
||||
qrCode := o.Code
|
||||
qCode := qrCode
|
||||
if qCode != "" {
|
||||
|
||||
if err := r.SetQueryParam("code", qCode); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if o.State != nil {
|
||||
|
||||
// query param state
|
||||
var qrState string
|
||||
|
||||
if o.State != nil {
|
||||
qrState = *o.State
|
||||
}
|
||||
qState := qrState
|
||||
if qState != "" {
|
||||
|
||||
if err := r.SetQueryParam("state", qState); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
208
rest_client_zrok/share/oauth_authenticate_responses.go
Normal file
@ -0,0 +1,208 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package share
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/strfmt"
|
||||
)
|
||||
|
||||
// OauthAuthenticateReader is a Reader for the OauthAuthenticate structure.
|
||||
type OauthAuthenticateReader struct {
|
||||
formats strfmt.Registry
|
||||
}
|
||||
|
||||
// ReadResponse reads a server response into the received o.
|
||||
func (o *OauthAuthenticateReader) ReadResponse(response runtime.ClientResponse, consumer runtime.Consumer) (interface{}, error) {
|
||||
switch response.Code() {
|
||||
case 200:
|
||||
result := NewOauthAuthenticateOK()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
case 302:
|
||||
result := NewOauthAuthenticateFound()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, result
|
||||
case 500:
|
||||
result := NewOauthAuthenticateInternalServerError()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, result
|
||||
default:
|
||||
return nil, runtime.NewAPIError("response status code does not match any response statuses defined for this endpoint in the swagger spec", response, response.Code())
|
||||
}
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateOK creates a OauthAuthenticateOK with default headers values
|
||||
func NewOauthAuthenticateOK() *OauthAuthenticateOK {
|
||||
return &OauthAuthenticateOK{}
|
||||
}
|
||||
|
||||
/*
|
||||
OauthAuthenticateOK describes a response with status code 200, with default header values.
|
||||
|
||||
testing
|
||||
*/
|
||||
type OauthAuthenticateOK struct {
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this oauth authenticate o k response has a 2xx status code
|
||||
func (o *OauthAuthenticateOK) IsSuccess() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this oauth authenticate o k response has a 3xx status code
|
||||
func (o *OauthAuthenticateOK) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this oauth authenticate o k response has a 4xx status code
|
||||
func (o *OauthAuthenticateOK) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this oauth authenticate o k response has a 5xx status code
|
||||
func (o *OauthAuthenticateOK) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this oauth authenticate o k response a status code equal to that given
|
||||
func (o *OauthAuthenticateOK) IsCode(code int) bool {
|
||||
return code == 200
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateOK) Error() string {
|
||||
return fmt.Sprintf("[GET /oauth/authorize][%d] oauthAuthenticateOK ", 200)
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateOK) String() string {
|
||||
return fmt.Sprintf("[GET /oauth/authorize][%d] oauthAuthenticateOK ", 200)
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateOK) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateFound creates a OauthAuthenticateFound with default headers values
|
||||
func NewOauthAuthenticateFound() *OauthAuthenticateFound {
|
||||
return &OauthAuthenticateFound{}
|
||||
}
|
||||
|
||||
/*
|
||||
OauthAuthenticateFound describes a response with status code 302, with default header values.
|
||||
|
||||
redirect back to share
|
||||
*/
|
||||
type OauthAuthenticateFound struct {
|
||||
|
||||
/* Redirect URL
|
||||
*/
|
||||
Location string
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this oauth authenticate found response has a 2xx status code
|
||||
func (o *OauthAuthenticateFound) IsSuccess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this oauth authenticate found response has a 3xx status code
|
||||
func (o *OauthAuthenticateFound) IsRedirect() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsClientError returns true when this oauth authenticate found response has a 4xx status code
|
||||
func (o *OauthAuthenticateFound) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this oauth authenticate found response has a 5xx status code
|
||||
func (o *OauthAuthenticateFound) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this oauth authenticate found response a status code equal to that given
|
||||
func (o *OauthAuthenticateFound) IsCode(code int) bool {
|
||||
return code == 302
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateFound) Error() string {
|
||||
return fmt.Sprintf("[GET /oauth/authorize][%d] oauthAuthenticateFound ", 302)
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateFound) String() string {
|
||||
return fmt.Sprintf("[GET /oauth/authorize][%d] oauthAuthenticateFound ", 302)
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateFound) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
// hydrates response header location
|
||||
hdrLocation := response.GetHeader("location")
|
||||
|
||||
if hdrLocation != "" {
|
||||
o.Location = hdrLocation
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateInternalServerError creates a OauthAuthenticateInternalServerError with default headers values
|
||||
func NewOauthAuthenticateInternalServerError() *OauthAuthenticateInternalServerError {
|
||||
return &OauthAuthenticateInternalServerError{}
|
||||
}
|
||||
|
||||
/*
|
||||
OauthAuthenticateInternalServerError describes a response with status code 500, with default header values.
|
||||
|
||||
internal server error
|
||||
*/
|
||||
type OauthAuthenticateInternalServerError struct {
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this oauth authenticate internal server error response has a 2xx status code
|
||||
func (o *OauthAuthenticateInternalServerError) IsSuccess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this oauth authenticate internal server error response has a 3xx status code
|
||||
func (o *OauthAuthenticateInternalServerError) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this oauth authenticate internal server error response has a 4xx status code
|
||||
func (o *OauthAuthenticateInternalServerError) IsClientError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsServerError returns true when this oauth authenticate internal server error response has a 5xx status code
|
||||
func (o *OauthAuthenticateInternalServerError) IsServerError() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsCode returns true when this oauth authenticate internal server error response a status code equal to that given
|
||||
func (o *OauthAuthenticateInternalServerError) IsCode(code int) bool {
|
||||
return code == 500
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateInternalServerError) Error() string {
|
||||
return fmt.Sprintf("[GET /oauth/authorize][%d] oauthAuthenticateInternalServerError ", 500)
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateInternalServerError) String() string {
|
||||
return fmt.Sprintf("[GET /oauth/authorize][%d] oauthAuthenticateInternalServerError ", 500)
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticateInternalServerError) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
return nil
|
||||
}
|
@ -41,6 +41,12 @@ func (o *ShareReader) ReadResponse(response runtime.ClientResponse, consumer run
|
||||
return nil, err
|
||||
}
|
||||
return nil, result
|
||||
case 422:
|
||||
result := NewShareUnprocessableEntity()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return nil, result
|
||||
case 500:
|
||||
result := NewShareInternalServerError()
|
||||
if err := result.readResponse(response, consumer, o.formats); err != nil {
|
||||
@ -217,6 +223,57 @@ func (o *ShareNotFound) readResponse(response runtime.ClientResponse, consumer r
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewShareUnprocessableEntity creates a ShareUnprocessableEntity with default headers values
|
||||
func NewShareUnprocessableEntity() *ShareUnprocessableEntity {
|
||||
return &ShareUnprocessableEntity{}
|
||||
}
|
||||
|
||||
/*
|
||||
ShareUnprocessableEntity describes a response with status code 422, with default header values.
|
||||
|
||||
unprocessable
|
||||
*/
|
||||
type ShareUnprocessableEntity struct {
|
||||
}
|
||||
|
||||
// IsSuccess returns true when this share unprocessable entity response has a 2xx status code
|
||||
func (o *ShareUnprocessableEntity) IsSuccess() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsRedirect returns true when this share unprocessable entity response has a 3xx status code
|
||||
func (o *ShareUnprocessableEntity) IsRedirect() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsClientError returns true when this share unprocessable entity response has a 4xx status code
|
||||
func (o *ShareUnprocessableEntity) IsClientError() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// IsServerError returns true when this share unprocessable entity response has a 5xx status code
|
||||
func (o *ShareUnprocessableEntity) IsServerError() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCode returns true when this share unprocessable entity response a status code equal to that given
|
||||
func (o *ShareUnprocessableEntity) IsCode(code int) bool {
|
||||
return code == 422
|
||||
}
|
||||
|
||||
func (o *ShareUnprocessableEntity) Error() string {
|
||||
return fmt.Sprintf("[POST /share][%d] shareUnprocessableEntity ", 422)
|
||||
}
|
||||
|
||||
func (o *ShareUnprocessableEntity) String() string {
|
||||
return fmt.Sprintf("[POST /share][%d] shareUnprocessableEntity ", 422)
|
||||
}
|
||||
|
||||
func (o *ShareUnprocessableEntity) readResponse(response runtime.ClientResponse, consumer runtime.Consumer, formats strfmt.Registry) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewShareInternalServerError creates a ShareInternalServerError with default headers values
|
||||
func NewShareInternalServerError() *ShareInternalServerError {
|
||||
return &ShareInternalServerError{}
|
||||
|
@ -40,6 +40,16 @@ type ShareRequest struct {
|
||||
// frontend selection
|
||||
FrontendSelection []string `json:"frontendSelection"`
|
||||
|
||||
// oauth authorization check interval
|
||||
OauthAuthorizationCheckInterval string `json:"oauthAuthorizationCheckInterval,omitempty"`
|
||||
|
||||
// oauth email domains
|
||||
OauthEmailDomains []string `json:"oauthEmailDomains"`
|
||||
|
||||
// oauth provider
|
||||
// Enum: [github google]
|
||||
OauthProvider string `json:"oauthProvider,omitempty"`
|
||||
|
||||
// reserved
|
||||
Reserved bool `json:"reserved,omitempty"`
|
||||
|
||||
@ -60,6 +70,10 @@ func (m *ShareRequest) Validate(formats strfmt.Registry) error {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateOauthProvider(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
if err := m.validateShareMode(formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
@ -147,6 +161,48 @@ func (m *ShareRequest) validateBackendMode(formats strfmt.Registry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
var shareRequestTypeOauthProviderPropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
var res []string
|
||||
if err := json.Unmarshal([]byte(`["github","google"]`), &res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
for _, v := range res {
|
||||
shareRequestTypeOauthProviderPropEnum = append(shareRequestTypeOauthProviderPropEnum, v)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
// ShareRequestOauthProviderGithub captures enum value "github"
|
||||
ShareRequestOauthProviderGithub string = "github"
|
||||
|
||||
// ShareRequestOauthProviderGoogle captures enum value "google"
|
||||
ShareRequestOauthProviderGoogle string = "google"
|
||||
)
|
||||
|
||||
// prop value enum
|
||||
func (m *ShareRequest) validateOauthProviderEnum(path, location string, value string) error {
|
||||
if err := validate.EnumCase(path, location, value, shareRequestTypeOauthProviderPropEnum, true); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *ShareRequest) validateOauthProvider(formats strfmt.Registry) error {
|
||||
if swag.IsZero(m.OauthProvider) { // not required
|
||||
return nil
|
||||
}
|
||||
|
||||
// value enum
|
||||
if err := m.validateOauthProviderEnum("oauthProvider", "body", m.OauthProvider); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var shareRequestTypeShareModePropEnum []interface{}
|
||||
|
||||
func init() {
|
||||
|
@ -865,6 +865,9 @@ func init() {
|
||||
"404": {
|
||||
"description": "not found"
|
||||
},
|
||||
"422": {
|
||||
"description": "unprocessable"
|
||||
},
|
||||
"500": {
|
||||
"description": "internal server error",
|
||||
"schema": {
|
||||
@ -1484,6 +1487,22 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"oauthAuthorizationCheckInterval": {
|
||||
"type": "string"
|
||||
},
|
||||
"oauthEmailDomains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"oauthProvider": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"github",
|
||||
"google"
|
||||
]
|
||||
},
|
||||
"reserved": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@ -2462,6 +2481,9 @@ func init() {
|
||||
"404": {
|
||||
"description": "not found"
|
||||
},
|
||||
"422": {
|
||||
"description": "unprocessable"
|
||||
},
|
||||
"500": {
|
||||
"description": "internal server error",
|
||||
"schema": {
|
||||
@ -3081,6 +3103,22 @@ func init() {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"oauthAuthorizationCheckInterval": {
|
||||
"type": "string"
|
||||
},
|
||||
"oauthEmailDomains": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"oauthProvider": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"github",
|
||||
"google"
|
||||
]
|
||||
},
|
||||
"reserved": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
56
rest_server_zrok/operations/share/oauth_authenticate.go
Normal file
@ -0,0 +1,56 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package share
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
)
|
||||
|
||||
// OauthAuthenticateHandlerFunc turns a function with the right signature into a oauth authenticate handler
|
||||
type OauthAuthenticateHandlerFunc func(OauthAuthenticateParams) middleware.Responder
|
||||
|
||||
// Handle executing the request and returning a response
|
||||
func (fn OauthAuthenticateHandlerFunc) Handle(params OauthAuthenticateParams) middleware.Responder {
|
||||
return fn(params)
|
||||
}
|
||||
|
||||
// OauthAuthenticateHandler interface for that can handle valid oauth authenticate params
|
||||
type OauthAuthenticateHandler interface {
|
||||
Handle(OauthAuthenticateParams) middleware.Responder
|
||||
}
|
||||
|
||||
// NewOauthAuthenticate creates a new http.Handler for the oauth authenticate operation
|
||||
func NewOauthAuthenticate(ctx *middleware.Context, handler OauthAuthenticateHandler) *OauthAuthenticate {
|
||||
return &OauthAuthenticate{Context: ctx, Handler: handler}
|
||||
}
|
||||
|
||||
/*
|
||||
OauthAuthenticate swagger:route GET /oauth/authorize share oauthAuthenticate
|
||||
|
||||
OauthAuthenticate oauth authenticate API
|
||||
*/
|
||||
type OauthAuthenticate struct {
|
||||
Context *middleware.Context
|
||||
Handler OauthAuthenticateHandler
|
||||
}
|
||||
|
||||
func (o *OauthAuthenticate) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
|
||||
route, rCtx, _ := o.Context.RouteInfo(r)
|
||||
if rCtx != nil {
|
||||
*r = *rCtx
|
||||
}
|
||||
var Params = NewOauthAuthenticateParams()
|
||||
if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params
|
||||
o.Context.Respond(rw, r, route.Produces, route, err)
|
||||
return
|
||||
}
|
||||
|
||||
res := o.Handler.Handle(Params) // actually handle the request
|
||||
o.Context.Respond(rw, r, route.Produces, route, res)
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package share
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/errors"
|
||||
"github.com/go-openapi/runtime"
|
||||
"github.com/go-openapi/runtime/middleware"
|
||||
"github.com/go-openapi/strfmt"
|
||||
"github.com/go-openapi/validate"
|
||||
)
|
||||
|
||||
// NewOauthAuthenticateParams creates a new OauthAuthenticateParams object
|
||||
//
|
||||
// There are no default values defined in the spec.
|
||||
func NewOauthAuthenticateParams() OauthAuthenticateParams {
|
||||
|
||||
return OauthAuthenticateParams{}
|
||||
}
|
||||
|
||||
// OauthAuthenticateParams contains all the bound params for the oauth authenticate operation
|
||||
// typically these are obtained from a http.Request
|
||||
//
|
||||
// swagger:parameters oauthAuthenticate
|
||||
type OauthAuthenticateParams struct {
|
||||
|
||||
// HTTP Request Object
|
||||
HTTPRequest *http.Request `json:"-"`
|
||||
|
||||
/*
|
||||
Required: true
|
||||
In: query
|
||||
*/
|
||||
Code string
|
||||
/*
|
||||
In: query
|
||||
*/
|
||||
State *string
|
||||
}
|
||||
|
||||
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
|
||||
// for simple values it will use straight method calls.
|
||||
//
|
||||
// To ensure default values, the struct must have been initialized with NewOauthAuthenticateParams() beforehand.
|
||||
func (o *OauthAuthenticateParams) BindRequest(r *http.Request, route *middleware.MatchedRoute) error {
|
||||
var res []error
|
||||
|
||||
o.HTTPRequest = r
|
||||
|
||||
qs := runtime.Values(r.URL.Query())
|
||||
|
||||
qCode, qhkCode, _ := qs.GetOK("code")
|
||||
if err := o.bindCode(qCode, qhkCode, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
|
||||
qState, qhkState, _ := qs.GetOK("state")
|
||||
if err := o.bindState(qState, qhkState, route.Formats); err != nil {
|
||||
res = append(res, err)
|
||||
}
|
||||
if len(res) > 0 {
|
||||
return errors.CompositeValidationError(res...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindCode binds and validates parameter Code from query.
|
||||
func (o *OauthAuthenticateParams) bindCode(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
if !hasKey {
|
||||
return errors.Required("code", "query", rawData)
|
||||
}
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: true
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if err := validate.RequiredString("code", "query", raw); err != nil {
|
||||
return err
|
||||
}
|
||||
o.Code = raw
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// bindState binds and validates parameter State from query.
|
||||
func (o *OauthAuthenticateParams) bindState(rawData []string, hasKey bool, formats strfmt.Registry) error {
|
||||
var raw string
|
||||
if len(rawData) > 0 {
|
||||
raw = rawData[len(rawData)-1]
|
||||
}
|
||||
|
||||
// Required: false
|
||||
// AllowEmptyValue: false
|
||||
|
||||
if raw == "" { // empty values pass all other validations
|
||||
return nil
|
||||
}
|
||||
o.State = &raw
|
||||
|
||||
return nil
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package share
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the swagger generate command
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/go-openapi/runtime"
|
||||
)
|
||||
|
||||
// OauthAuthenticateOKCode is the HTTP code returned for type OauthAuthenticateOK
|
||||
const OauthAuthenticateOKCode int = 200
|
||||
|
||||
/*
|
||||
OauthAuthenticateOK testing
|
||||
|
||||
swagger:response oauthAuthenticateOK
|
||||
*/
|
||||
type OauthAuthenticateOK struct {
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateOK creates OauthAuthenticateOK with default headers values
|
||||
func NewOauthAuthenticateOK() *OauthAuthenticateOK {
|
||||
|
||||
return &OauthAuthenticateOK{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *OauthAuthenticateOK) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(200)
|
||||
}
|
||||
|
||||
// OauthAuthenticateFoundCode is the HTTP code returned for type OauthAuthenticateFound
|
||||
const OauthAuthenticateFoundCode int = 302
|
||||
|
||||
/*
|
||||
OauthAuthenticateFound redirect back to share
|
||||
|
||||
swagger:response oauthAuthenticateFound
|
||||
*/
|
||||
type OauthAuthenticateFound struct {
|
||||
/*Redirect URL
|
||||
|
||||
*/
|
||||
Location string `json:"location"`
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateFound creates OauthAuthenticateFound with default headers values
|
||||
func NewOauthAuthenticateFound() *OauthAuthenticateFound {
|
||||
|
||||
return &OauthAuthenticateFound{}
|
||||
}
|
||||
|
||||
// WithLocation adds the location to the oauth authenticate found response
|
||||
func (o *OauthAuthenticateFound) WithLocation(location string) *OauthAuthenticateFound {
|
||||
o.Location = location
|
||||
return o
|
||||
}
|
||||
|
||||
// SetLocation sets the location to the oauth authenticate found response
|
||||
func (o *OauthAuthenticateFound) SetLocation(location string) {
|
||||
o.Location = location
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *OauthAuthenticateFound) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
// response header location
|
||||
|
||||
location := o.Location
|
||||
if location != "" {
|
||||
rw.Header().Set("location", location)
|
||||
}
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(302)
|
||||
}
|
||||
|
||||
// OauthAuthenticateInternalServerErrorCode is the HTTP code returned for type OauthAuthenticateInternalServerError
|
||||
const OauthAuthenticateInternalServerErrorCode int = 500
|
||||
|
||||
/*
|
||||
OauthAuthenticateInternalServerError internal server error
|
||||
|
||||
swagger:response oauthAuthenticateInternalServerError
|
||||
*/
|
||||
type OauthAuthenticateInternalServerError struct {
|
||||
}
|
||||
|
||||
// NewOauthAuthenticateInternalServerError creates OauthAuthenticateInternalServerError with default headers values
|
||||
func NewOauthAuthenticateInternalServerError() *OauthAuthenticateInternalServerError {
|
||||
|
||||
return &OauthAuthenticateInternalServerError{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *OauthAuthenticateInternalServerError) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(500)
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
// Code generated by go-swagger; DO NOT EDIT.
|
||||
|
||||
package share
|
||||
|
||||
// This file was generated by the swagger tool.
|
||||
// Editing this file might prove futile when you re-run the generate command
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
golangswaggerpaths "path"
|
||||
)
|
||||
|
||||
// OauthAuthenticateURL generates an URL for the oauth authenticate operation
|
||||
type OauthAuthenticateURL struct {
|
||||
Code string
|
||||
State *string
|
||||
|
||||
_basePath string
|
||||
// avoid unkeyed usage
|
||||
_ struct{}
|
||||
}
|
||||
|
||||
// WithBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *OauthAuthenticateURL) WithBasePath(bp string) *OauthAuthenticateURL {
|
||||
o.SetBasePath(bp)
|
||||
return o
|
||||
}
|
||||
|
||||
// SetBasePath sets the base path for this url builder, only required when it's different from the
|
||||
// base path specified in the swagger spec.
|
||||
// When the value of the base path is an empty string
|
||||
func (o *OauthAuthenticateURL) SetBasePath(bp string) {
|
||||
o._basePath = bp
|
||||
}
|
||||
|
||||
// Build a url path and query string
|
||||
func (o *OauthAuthenticateURL) Build() (*url.URL, error) {
|
||||
var _result url.URL
|
||||
|
||||
var _path = "/oauth/authorize"
|
||||
|
||||
_basePath := o._basePath
|
||||
if _basePath == "" {
|
||||
_basePath = "/api/v1"
|
||||
}
|
||||
_result.Path = golangswaggerpaths.Join(_basePath, _path)
|
||||
|
||||
qs := make(url.Values)
|
||||
|
||||
codeQ := o.Code
|
||||
if codeQ != "" {
|
||||
qs.Set("code", codeQ)
|
||||
}
|
||||
|
||||
var stateQ string
|
||||
if o.State != nil {
|
||||
stateQ = *o.State
|
||||
}
|
||||
if stateQ != "" {
|
||||
qs.Set("state", stateQ)
|
||||
}
|
||||
|
||||
_result.RawQuery = qs.Encode()
|
||||
|
||||
return &_result, nil
|
||||
}
|
||||
|
||||
// Must is a helper function to panic when the url builder returns an error
|
||||
func (o *OauthAuthenticateURL) Must(u *url.URL, err error) *url.URL {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if u == nil {
|
||||
panic("url can't be nil")
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
// String returns the string representation of the path with query string
|
||||
func (o *OauthAuthenticateURL) String() string {
|
||||
return o.Must(o.Build()).String()
|
||||
}
|
||||
|
||||
// BuildFull builds a full url with scheme, host, path and query string
|
||||
func (o *OauthAuthenticateURL) BuildFull(scheme, host string) (*url.URL, error) {
|
||||
if scheme == "" {
|
||||
return nil, errors.New("scheme is required for a full url on OauthAuthenticateURL")
|
||||
}
|
||||
if host == "" {
|
||||
return nil, errors.New("host is required for a full url on OauthAuthenticateURL")
|
||||
}
|
||||
|
||||
base, err := o.Build()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base.Scheme = scheme
|
||||
base.Host = host
|
||||
return base, nil
|
||||
}
|
||||
|
||||
// StringFull returns the string representation of a complete url
|
||||
func (o *OauthAuthenticateURL) StringFull(scheme, host string) string {
|
||||
return o.Must(o.BuildFull(scheme, host)).String()
|
||||
}
|
@ -108,6 +108,31 @@ func (o *ShareNotFound) WriteResponse(rw http.ResponseWriter, producer runtime.P
|
||||
rw.WriteHeader(404)
|
||||
}
|
||||
|
||||
// ShareUnprocessableEntityCode is the HTTP code returned for type ShareUnprocessableEntity
|
||||
const ShareUnprocessableEntityCode int = 422
|
||||
|
||||
/*
|
||||
ShareUnprocessableEntity unprocessable
|
||||
|
||||
swagger:response shareUnprocessableEntity
|
||||
*/
|
||||
type ShareUnprocessableEntity struct {
|
||||
}
|
||||
|
||||
// NewShareUnprocessableEntity creates ShareUnprocessableEntity with default headers values
|
||||
func NewShareUnprocessableEntity() *ShareUnprocessableEntity {
|
||||
|
||||
return &ShareUnprocessableEntity{}
|
||||
}
|
||||
|
||||
// WriteResponse to the client
|
||||
func (o *ShareUnprocessableEntity) WriteResponse(rw http.ResponseWriter, producer runtime.Producer) {
|
||||
|
||||
rw.Header().Del(runtime.HeaderContentType) //Remove Content-Type on empty responses
|
||||
|
||||
rw.WriteHeader(422)
|
||||
}
|
||||
|
||||
// ShareInternalServerErrorCode is the HTTP code returned for type ShareInternalServerError
|
||||
const ShareInternalServerErrorCode int = 500
|
||||
|
||||
|
@ -4,26 +4,35 @@ import "github.com/pkg/errors"
|
||||
|
||||
const ZrokProxyConfig = "zrok.proxy.v1"
|
||||
|
||||
type ProxyConfig struct {
|
||||
AuthScheme AuthScheme `json:"auth_scheme"`
|
||||
BasicAuth *BasicAuth `json:"basic_auth"`
|
||||
type FrontendConfig struct {
|
||||
AuthScheme AuthScheme `json:"auth_scheme"`
|
||||
BasicAuth *BasicAuthConfig `json:"basic_auth"`
|
||||
OauthAuth *OauthConfig `json:"oauth"`
|
||||
}
|
||||
|
||||
type BasicAuth struct {
|
||||
Users []*AuthUser `json:"users"`
|
||||
type BasicAuthConfig struct {
|
||||
Users []*AuthUserConfig `json:"users"`
|
||||
}
|
||||
|
||||
type AuthUser struct {
|
||||
type AuthUserConfig struct {
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type OauthConfig struct {
|
||||
Provider string `json:"provider"`
|
||||
EmailDomains []string `json:"email_domains"`
|
||||
AuthorizationCheckInterval string `json:"authorization_check_interval"`
|
||||
}
|
||||
|
||||
func ParseAuthScheme(authScheme string) (AuthScheme, error) {
|
||||
switch authScheme {
|
||||
case string(None):
|
||||
return None, nil
|
||||
case string(Basic):
|
||||
return Basic, nil
|
||||
case string(Oauth):
|
||||
return Oauth, nil
|
||||
default:
|
||||
return None, errors.Errorf("unknown auth scheme '%v'", authScheme)
|
||||
}
|
||||
|
16
sdk/model.go
@ -1,5 +1,7 @@
|
||||
package sdk
|
||||
|
||||
import "time"
|
||||
|
||||
type BackendMode string
|
||||
|
||||
const (
|
||||
@ -18,11 +20,14 @@ const (
|
||||
)
|
||||
|
||||
type ShareRequest struct {
|
||||
BackendMode BackendMode
|
||||
ShareMode ShareMode
|
||||
Frontends []string
|
||||
Auth []string
|
||||
Target string
|
||||
BackendMode BackendMode
|
||||
ShareMode ShareMode
|
||||
Target string
|
||||
Frontends []string
|
||||
BasicAuth []string
|
||||
OauthProvider string
|
||||
OauthEmailDomains []string
|
||||
OauthAuthorizationCheckInterval time.Duration
|
||||
}
|
||||
|
||||
type Share struct {
|
||||
@ -56,4 +61,5 @@ type AuthScheme string
|
||||
const (
|
||||
None AuthScheme = "none"
|
||||
Basic AuthScheme = "basic"
|
||||
Oauth AuthScheme = "oauth"
|
||||
)
|
||||
|
27
sdk/share.go
@ -26,18 +26,22 @@ func CreateShare(root env_core.Root, request *ShareRequest) (*Share, error) {
|
||||
return nil, errors.Errorf("unknown share mode '%v'", request.ShareMode)
|
||||
}
|
||||
|
||||
if len(request.Auth) > 0 {
|
||||
if len(request.BasicAuth) > 0 {
|
||||
out.Body.AuthScheme = string(Basic)
|
||||
for _, pair := range request.Auth {
|
||||
tokens := strings.Split(pair, ":")
|
||||
for _, basicAuthUser := range request.BasicAuth {
|
||||
tokens := strings.Split(basicAuthUser, ":")
|
||||
if len(tokens) == 2 {
|
||||
out.Body.AuthUsers = append(out.Body.AuthUsers, &rest_model_zrok.AuthUser{Username: strings.TrimSpace(tokens[0]), Password: strings.TrimSpace(tokens[1])})
|
||||
} else {
|
||||
return nil, errors.Errorf("invalid username:password pair '%v'", pair)
|
||||
return nil, errors.Errorf("invalid username:password '%v'", basicAuthUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if request.OauthProvider != "" {
|
||||
out.Body.AuthScheme = string(Oauth)
|
||||
}
|
||||
|
||||
zrok, err := root.Client()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error getting zrok client")
|
||||
@ -70,12 +74,15 @@ func newPrivateShare(root env_core.Root, request *ShareRequest) *share.SharePara
|
||||
func newPublicShare(root env_core.Root, request *ShareRequest) *share.ShareParams {
|
||||
req := share.NewShareParams()
|
||||
req.Body = &rest_model_zrok.ShareRequest{
|
||||
EnvZID: root.Environment().ZitiIdentity,
|
||||
ShareMode: string(request.ShareMode),
|
||||
FrontendSelection: request.Frontends,
|
||||
BackendMode: string(request.BackendMode),
|
||||
BackendProxyEndpoint: request.Target,
|
||||
AuthScheme: string(None),
|
||||
EnvZID: root.Environment().ZitiIdentity,
|
||||
ShareMode: string(request.ShareMode),
|
||||
FrontendSelection: request.Frontends,
|
||||
BackendMode: string(request.BackendMode),
|
||||
BackendProxyEndpoint: request.Target,
|
||||
AuthScheme: string(None),
|
||||
OauthEmailDomains: request.OauthEmailDomains,
|
||||
OauthProvider: request.OauthProvider,
|
||||
OauthAuthorizationCheckInterval: request.OauthAuthorizationCheckInterval.String(),
|
||||
}
|
||||
return req
|
||||
}
|
||||
|
@ -577,6 +577,8 @@ paths:
|
||||
description: unauthorized
|
||||
404:
|
||||
description: not found
|
||||
422:
|
||||
description: unprocessable
|
||||
500:
|
||||
description: internal server error
|
||||
schema:
|
||||
@ -978,6 +980,15 @@ definitions:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/definitions/authUser"
|
||||
oauthProvider:
|
||||
type: string
|
||||
enum: ["github", "google"]
|
||||
oauthEmailDomains:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
oauthAuthorizationCheckInterval:
|
||||
type: string
|
||||
reserved:
|
||||
type: boolean
|
||||
|
||||
|
@ -253,6 +253,9 @@
|
||||
* @property {string} backendProxyEndpoint
|
||||
* @property {string} authScheme
|
||||
* @property {module:types.authUser[]} authUsers
|
||||
* @property {string} oauthProvider
|
||||
* @property {string[]} oauthEmailDomains
|
||||
* @property {string} oauthAuthorizationCheckInterval
|
||||
* @property {boolean} reserved
|
||||
*/
|
||||
|
||||
|