updates to the oauth work

This commit is contained in:
Ziti-Ci 2023-09-05 09:55:55 -05:00
parent 18424a1b48
commit 2b0dc71f93
No known key found for this signature in database
GPG Key ID: 367B7C7EBD84A8BD
23 changed files with 1136 additions and 187 deletions

View File

@ -6,6 +6,7 @@ import (
"os/signal"
"strings"
"syscall"
"time"
tea "github.com/charmbracelet/bubbletea"
"github.com/go-openapi/runtime"
@ -28,14 +29,15 @@ func init() {
}
type sharePublicCommand struct {
basicAuth []string
frontendSelection []string
backendMode string
headless bool
insecure bool
oauthProvider string
oauthEmailDomains []string
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 {
@ -53,6 +55,8 @@ func newSharePublicCommand() *sharePublicCommand {
cmd.Flags().StringVar(&command.oauthProvider, "provider", "", "Provider to authenticate against with oauth")
cmd.Flags().StringArrayVar(&command.oauthEmailDomains, "oauth-domains", []string{}, "Valid email domains for oauth authentication")
cmd.Flags().DurationVar(&command.oauthCheckInterval, "oauth-check-interval", 3*time.Hour, "Max lifetime for oauth validation. Will force a recheck once time elapses for session")
cmd.MarkFlagsMutuallyExclusive("basic-auth", "provider")
cmd.Run = command.run
return command
@ -132,9 +136,8 @@ func (cmd *sharePublicCommand) run(_ *cobra.Command, args []string) {
if cmd.oauthProvider != "" {
req.Body.AuthScheme = string(model.Oauth)
req.Body.OauthProvider = cmd.oauthProvider
}
if len(cmd.oauthEmailDomains) > 0 {
req.Body.OauthEmailDomains = cmd.oauthEmailDomains
req.Body.OauthAuthorizationCheckInterval = cmd.oauthCheckInterval.String()
}
resp, err := zrok.Share.Share(req, auth)
if err != nil {

View File

@ -70,7 +70,6 @@ func Run(inCfg *config.Config) error {
api.MetadataOverviewHandler = newOverviewHandler()
api.MetadataVersionHandler = metadata.VersionHandlerFunc(versionHandler)
api.ShareAccessHandler = newAccessHandler()
api.ShareOauthAuthenticateHandler = newOauthHandler()
api.ShareShareHandler = newShareHandler()
api.ShareUnaccessHandler = newUnaccessHandler()
api.ShareUnshareHandler = newUnshareHandler()

View File

@ -1,87 +0,0 @@
package controller
import (
"context"
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"net/http"
"net/url"
"strings"
"github.com/openziti/zrok/controller/oauth"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/cognitoidentityprovider"
"github.com/go-openapi/runtime/middleware"
"github.com/openziti/zrok/rest_server_zrok/operations/share"
"github.com/sirupsen/logrus"
)
type oauthHandler struct{}
func newOauthHandler() *oauthHandler {
return &oauthHandler{}
}
func (h *oauthHandler) Handle(params share.OauthAuthenticateParams) middleware.Responder {
ghandle := oauth.NewGoogleOauthHandler()
return ghandle.Handle(params)
awsUrl := "https:///oauth2/token" // COGNITO URL OR WHATEVER OAUTH PROVIDER URL
clientId := "" // PROVIDER CLIENT ID
secret := "" // PROVIDER CLIENT SECRET
auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", clientId, secret)))
grant := "authorization_code"
redirectUri := "http://localhost:18080/api/v1/oauth/authorize"
// scope := "email"
data := url.Values{}
data.Set("client_id", clientId)
data.Set("grant_type", grant)
// data.Set("scope", scope)
data.Set("code", params.Code)
data.Set("redirect_uri", redirectUri)
encodedData := data.Encode()
c := http.Client{}
req := &http.Request{}
req.Method = http.MethodPost
req.URL, _ = url.Parse(awsUrl)
req.Body = io.NopCloser(strings.NewReader(encodedData))
req.Header = http.Header{}
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Authorization", fmt.Sprintf("Basic %s", auth))
resp, err := c.Do(req)
// resp, err := http.Post(awsUrl, "application/x-www-form-urlencoded", strings.NewReader(encodedData))
logrus.Error(err)
logrus.Error(resp)
b, err := ioutil.ReadAll(resp.Body)
logrus.Error(err)
logrus.Error(string(b))
//user, err := cog.GetUser(&cognitoidentityprovider.GetUserInput{
// AccessToken: aws.String(params.Code),
//})
//if err != nil {
// logrus.Error(err)
//}
//logrus.Error(user)
logrus.Error("--------------")
return share.NewOauthAuthenticateOK()
}
func old(params share.OauthAuthenticateParams) {
sdkConfig, err := config.LoadDefaultConfig(context.TODO())
sdkConfig.Region = "us-east-1"
if err != nil {
fmt.Println("Couldn't load default configuration. Have you set up your AWS account?")
fmt.Println(err)
// return share.NewOauthAuthenticateOK()
}
cog := cognitoidentityprovider.NewFromConfig(sdkConfig)
user, err := cog.GetUser(context.TODO(), &cognitoidentityprovider.GetUserInput{
AccessToken: &params.Code,
})
logrus.Error(err)
logrus.Error(user)
}

View File

@ -9,6 +9,29 @@ type Config struct {
Identity string
Address string
HostMatch string
Oauth *OauthConfig
}
type OauthConfig struct {
Port int
RedirectUrl string
HashKeyRaw string
Providers []*OauthProviderSecrets
}
func (oc *OauthConfig) GetProvider(name string) *OauthProviderSecrets {
for _, provider := range oc.Providers {
if provider.Name == name {
return provider
}
}
return nil
}
type OauthProviderSecrets struct {
Name string
ClientId string
ClientSecret string
}
func DefaultConfig() *Config {

View File

@ -0,0 +1,151 @@
package publicProxy
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
"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"
)
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"
port := cfg.Port
redirectUrl := fmt.Sprintf("%s://%s", scheme, cfg.RedirectUrl)
rpConfig := &oauth2.Config{
ClientID: clientID,
ClientSecret: providerCfg.ClientSecret,
RedirectURL: fmt.Sprintf("%v:%v%v", redirectUrl, port, callbackPath),
Scopes: []string{"user:email"},
Endpoint: githubOAuth.Endpoint,
}
key := []byte(cfg.HashKeyRaw)
cookieHandler := zhttp.NewCookieHandler(key, key, zhttp.WithUnsecure(), zhttp.WithDomain(cfg.RedirectUrl))
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"`
Share string `json:"share"`
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) {
rp.AuthURLHandler(func() string {
id := uuid.New().String()
t := jwt.NewWithClaims(jwt.SigningMethodHS256, IntermediateJWT{
id,
r.URL.Query().Get("share"),
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("Get: " + err.Error() + "\n")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
response, err := io.ReadAll(resp.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
rDat := []githubUserResp{}
err = json.Unmarshal(response, &rDat)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
primaryEmail := ""
for _, email := range rDat {
if email.Primary {
primaryEmail = email.Email
break
}
}
SetZrokCookie(w, primaryEmail, tokens.AccessToken, "github", 3*time.Hour, key)
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
}
http.Redirect(w, r, fmt.Sprintf("%s://%s.%s:8080", scheme, token.Claims.(*IntermediateJWT).Share, cfg.RedirectUrl), http.StatusFound)
}
http.Handle(callbackPath, rp.CodeExchangeHandler(getEmail, relyingParty))
return nil
}

View File

@ -0,0 +1,130 @@
package publicProxy
import (
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"time"
"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"
)
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"
port := cfg.Port
redirectUrl := fmt.Sprintf("%s://%s", scheme, cfg.RedirectUrl)
rpConfig := &oauth2.Config{
ClientID: clientID,
ClientSecret: providerCfg.ClientSecret,
RedirectURL: fmt.Sprintf("%v:%v%v", redirectUrl, port, callbackPath),
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email"},
Endpoint: googleOauth.Endpoint,
}
key := []byte(cfg.HashKeyRaw)
cookieHandler := zhttp.NewCookieHandler(key, key, zhttp.WithUnsecure(), zhttp.WithDomain(cfg.RedirectUrl))
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"`
Share string `json:"share"`
jwt.RegisteredClaims
}
type googleOauthEmailResp struct {
Email string
}
authHandlerWithQueryState := func(party rp.RelyingParty) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
rp.AuthURLHandler(func() string {
id := uuid.New().String()
t := jwt.NewWithClaims(jwt.SigningMethodHS256, IntermediateJWT{
id,
r.URL.Query().Get("share"),
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("Get: " + err.Error() + "\n")
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
response, err := io.ReadAll(resp.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
rDat := googleOauthEmailResp{}
err = json.Unmarshal(response, &rDat)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
SetZrokCookie(w, rDat.Email, tokens.AccessToken, "google", 3*time.Hour, key)
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
}
http.Redirect(w, r, fmt.Sprintf("%s://%s.%s:8080", scheme, token.Claims.(*IntermediateJWT).Share, cfg.RedirectUrl), http.StatusFound)
}
http.Handle(callbackPath, rp.CodeExchangeHandler(getEmail, relyingParty))
return nil
}

View File

@ -8,16 +8,20 @@ import (
"net/http/httputil"
"net/url"
"strings"
"time"
"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/model"
"github.com/openziti/zrok/util"
"github.com/openziti/zrok/zrokdir"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
zhttp "github.com/zitadel/oidc/v2/pkg/http"
)
type httpFrontend struct {
@ -49,7 +53,9 @@ func NewHTTP(cfg *Config) (*httpFrontend, error) {
return nil, err
}
proxy.Transport = zTransport
if err := configureOauthHandlers(context.Background(), cfg, false); err != nil {
return nil, err
}
handler := authHandler(util.NewProxyHandler(proxy), "zrok", cfg, zCtx)
return &httpFrontend{
cfg: cfg,
@ -125,9 +131,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, realm string, pcfg *Config, 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[model.ZrokProxyConfig]; found {
@ -183,17 +189,80 @@ func authHandler(handler http.Handler, realm string, cfg *Config, ctx ziti.Conte
handler.ServeHTTP(w, r)
case string(model.Oauth):
logrus.Debugf("auth scheme oauth '%v'", shrToken)
awsUrl := "https://oauth2/authorize" // COGNITO URL OR WHATEVER OAUTH PROVIDER URL
responseType := "code"
clientId := "" // PROVIDER CLIENT ID
scope := "email"
redirectUri := "http://localhost:18080/api/v1/oauth/authorize"
redirectUrl := fmt.Sprintf("%s?response_type=%s&client_id=%s&redirect_uri=%s&state=STATE&scope=%s", awsUrl, responseType, clientId, redirectUri, scope)
http.Redirect(w, r, redirectUrl, http.StatusFound)
handler.ServeHTTP(w, r)
return
if oauthCfg, found := cfg["oauth"]; found {
if provider, found := oauthCfg.(map[string]interface{})["provider"]; found {
cookie, err := r.Cookie("zrok-access")
if err != nil {
logrus.Errorf("Unable to get access cookie: %v", err)
http.Redirect(w, r, fmt.Sprintf("http://%s.%s:28080/%s/login?share=%s", shrToken, pcfg.HostMatch, provider.(string), shrToken), http.StatusFound)
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 pcfg.Oauth.HashKeyRaw, nil
})
if err != nil {
logrus.Errorf("Unable to parse JWT: %v", err)
http.Redirect(w, r, fmt.Sprintf("http://%s.%s:28080/%s/login?share=%s", shrToken, pcfg.HostMatch, provider.(string), shrToken), http.StatusFound)
return
}
claims := tkn.Claims.(*ZrokClaims)
if claims.Provider != provider {
logrus.Error("Provider mismatch. Redoing auth flow")
http.Redirect(w, r, fmt.Sprintf("http://%s.%s:28080/%s/login?share=%s", shrToken, pcfg.HostMatch, provider.(string), shrToken), http.StatusFound)
return
}
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
}
}
if claims.AuthorizationCheckInterval != authCheckInterval {
logrus.Error("Authorization check interval mismatch. Redoing auth flow")
http.Redirect(w, r, fmt.Sprintf("http://%s.%s:28080/%s/login?share=%s", shrToken, pcfg.HostMatch, provider.(string), shrToken), http.StatusFound)
return
}
if validDomains, found := oauthCfg.(map[string]interface{})["email_domains"]; found {
if castedDomains, ok := validDomains.([]interface{}); !ok {
logrus.Error("Invalid format for valid email domains")
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("Email not a valid 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)
@ -218,6 +287,53 @@ func authHandler(handler http.Handler, realm string, cfg *Config, ctx ziti.Conte
}
}
func configureOauthHandlers(ctx context.Context, cfg *Config, tls bool) error {
if cfg.Oauth == nil {
logrus.Info("No oauth config for access point. Skipping spin up.")
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, "0.0.0.0:28080")
return nil
}
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, 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: 3000,
Domain: "localzrok.io",
Path: "/",
Expires: time.Now().Add(checkInterval),
//Secure: true, //When tls gets added have this be configured on if tls
})
}
func writeUnauthorizedResponse(w http.ResponseWriter, realm string) {
w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`)
w.WriteHeader(401)

View File

@ -0,0 +1,6 @@
package unauthorizedUi
import "embed"
//go:embed index.html
var FS embed.FS

View 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
}
}
}

View 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>

View File

@ -3,3 +3,19 @@
# purposes, and will allow `Host` headers that match the configured DNS name to be routed through `zrok`.
#
host_match: zrok.io
#tls:
# cert_path: "/Path/To/Cert/zrok.crt"
# key_path: "/Path/To/Cert/zrok.key"
#oauth:
# port: 28080
# redirect_url: zrok.io
# hash_key_raw: "test1234test1234"
# providers:
# - name: google
# client_id: <client-id>
# client_secret: <client-secret>
# - name: github
# client_id: <client-id>
# client_secret: <client-secret>

25
go.mod
View File

@ -15,6 +15,7 @@ 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/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
@ -42,13 +43,17 @@ require (
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.4
github.com/wneessen/go-mail v0.2.7
golang.org/x/crypto v0.10.0
golang.org/x/net v0.11.0
github.com/zitadel/oidc/v2 v2.7.0
golang.org/x/crypto v0.11.0
golang.org/x/net v0.12.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
github.com/Jeffail/gabs v1.4.0 // indirect
github.com/antlr/antlr4/runtime/Go/antlr v0.0.0-20211106181442-e4c1a74c66bd // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
@ -81,8 +86,11 @@ require (
github.com/go-openapi/jsonpointer v0.19.6 // indirect
github.com/go-openapi/jsonreference v0.20.2 // indirect
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang/protobuf v1.5.3 // 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/inconshreveable/mousetrap v1.1.0 // indirect
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839 // indirect
github.com/josharian/intern v1.0.0 // indirect
@ -104,6 +112,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/opentracing/opentracing-go v1.2.0 // indirect
github.com/openziti/foundation/v2 v2.0.26 // indirect
@ -129,10 +138,12 @@ require (
go.opentelemetry.io/otel/metric v1.16.0 // indirect
go.opentelemetry.io/otel/trace v1.16.0 // indirect
golang.org/x/exp v0.0.0-20230425010034-47ecfdc1ba53 // indirect
golang.org/x/sys v0.9.0 // indirect
golang.org/x/term v0.9.0 // indirect
golang.org/x/text v0.10.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
golang.org/x/sys v0.10.0 // indirect
golang.org/x/term v0.10.0 // indirect
golang.org/x/text v0.11.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect

50
go.sum
View File

@ -24,6 +24,10 @@ cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvf
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
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=
@ -251,6 +255,8 @@ github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@ -264,6 +270,7 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@ -280,8 +287,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -298,6 +306,7 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
@ -325,6 +334,10 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
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 v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@ -367,6 +380,7 @@ github.com/jaevor/go-nanoid v1.3.0 h1:nD+iepesZS6pr3uOVf20vR9GdGgJW1HPaR46gtrxzk
github.com/jaevor/go-nanoid v1.3.0/go.mod h1:SI+jFaPuddYkqkVQoNGHs81navCtH388TcrH0RqFKgY=
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.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc=
github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@ -508,6 +522,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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
@ -583,6 +599,7 @@ github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFR
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE=
github.com/rubenv/sql-migrate v1.1.2 h1:9M6oj4e//owVVHYrFISmY9LBRw6gzkCNmD9MV36tZeQ=
github.com/rubenv/sql-migrate v1.1.2/go.mod h1:/7TZymwxN8VWumcIxw1jjHEcR1djpdkMHQPT4FWdnbQ=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@ -666,6 +683,8 @@ github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
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.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
@ -715,8 +734,8 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -799,8 +818,8 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
golang.org/x/net v0.11.0 h1:Gi2tvZIJyBtO9SDr1q9h5hEQCp/4L2RQ+ar0qjx2oNU=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@ -813,6 +832,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
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=
@ -900,14 +921,14 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.9.0 h1:GRRCnKYhdQrD8kfRAdQ6Zcw1P0OcELxGLKJvtjVMZ28=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -918,8 +939,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@ -1017,6 +1038,7 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
@ -1091,8 +1113,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@ -1103,6 +1125,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/square/go-jose.v2 v2.6.0 h1:NGk74WTnPKBNUhNzQX7PYcTLUjoq7mzKk2OKbvwk2iI=
gopkg.in/square/go-jose.v2 v2.6.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -65,7 +65,7 @@ type OauthAuthenticateParams struct {
Code string
// State.
State string
State *string
timeout time.Duration
Context context.Context
@ -132,13 +132,13 @@ func (o *OauthAuthenticateParams) SetCode(code string) {
}
// WithState adds the state to the oauth authenticate params
func (o *OauthAuthenticateParams) WithState(state string) *OauthAuthenticateParams {
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) {
func (o *OauthAuthenticateParams) SetState(state *string) {
o.State = state
}
@ -160,13 +160,20 @@ func (o *OauthAuthenticateParams) WriteToRequest(r runtime.ClientRequest, reg st
}
}
// query param state
qrState := o.State
qState := qrState
if qState != "" {
if o.State != nil {
if err := r.SetQueryParam("state", qState); err != nil {
return err
// 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
}
}
}

View File

@ -26,6 +26,12 @@ func (o *OauthAuthenticateReader) ReadResponse(response runtime.ClientResponse,
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 {
@ -88,6 +94,68 @@ func (o *OauthAuthenticateOK) readResponse(response runtime.ClientResponse, cons
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{}

View File

@ -40,11 +40,14 @@ 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: [amazon]
// Enum: [github google]
OauthProvider string `json:"oauthProvider,omitempty"`
// reserved
@ -159,7 +162,7 @@ var shareRequestTypeOauthProviderPropEnum []interface{}
func init() {
var res []string
if err := json.Unmarshal([]byte(`["amazon"]`), &res); err != nil {
if err := json.Unmarshal([]byte(`["github","google"]`), &res); err != nil {
panic(err)
}
for _, v := range res {
@ -169,8 +172,11 @@ func init() {
const (
// ShareRequestOauthProviderAmazon captures enum value "amazon"
ShareRequestOauthProviderAmazon string = "amazon"
// ShareRequestOauthProviderGithub captures enum value "github"
ShareRequestOauthProviderGithub string = "github"
// ShareRequestOauthProviderGoogle captures enum value "google"
ShareRequestOauthProviderGoogle string = "google"
)
// prop value enum

View File

@ -712,8 +712,7 @@ func init() {
{
"type": "string",
"name": "state",
"in": "query",
"required": true
"in": "query"
},
{
"type": "string",
@ -726,6 +725,15 @@ func init() {
"200": {
"description": "testing"
},
"302": {
"description": "redirect back to share",
"headers": {
"location": {
"type": "string",
"description": "Redirect URL"
}
}
},
"500": {
"description": "internal server error"
}
@ -1516,6 +1524,9 @@ func init() {
"type": "string"
}
},
"oauthAuthorizationCheckInterval": {
"type": "string"
},
"oauthEmailDomains": {
"type": "array",
"items": {
@ -1525,7 +1536,8 @@ func init() {
"oauthProvider": {
"type": "string",
"enum": [
"amazon"
"github",
"google"
]
},
"reserved": {
@ -2353,8 +2365,7 @@ func init() {
{
"type": "string",
"name": "state",
"in": "query",
"required": true
"in": "query"
},
{
"type": "string",
@ -2367,6 +2378,15 @@ func init() {
"200": {
"description": "testing"
},
"302": {
"description": "redirect back to share",
"headers": {
"location": {
"type": "string",
"description": "Redirect URL"
}
}
},
"500": {
"description": "internal server error"
}
@ -3157,6 +3177,9 @@ func init() {
"type": "string"
}
},
"oauthAuthorizationCheckInterval": {
"type": "string"
},
"oauthEmailDomains": {
"type": "array",
"items": {
@ -3166,7 +3189,8 @@ func init() {
"oauthProvider": {
"type": "string",
"enum": [
"amazon"
"github",
"google"
]
},
"reserved": {

View File

@ -38,10 +38,9 @@ type OauthAuthenticateParams struct {
*/
Code string
/*
Required: true
In: query
*/
State string
State *string
}
// BindRequest both binds and validates a request, it assumes that complex things implement a Validatable(strfmt.Registry) error interface
@ -93,21 +92,18 @@ func (o *OauthAuthenticateParams) bindCode(rawData []string, hasKey bool, format
// bindState binds and validates parameter State from query.
func (o *OauthAuthenticateParams) bindState(rawData []string, hasKey bool, formats strfmt.Registry) error {
if !hasKey {
return errors.Required("state", "query", rawData)
}
var raw string
if len(rawData) > 0 {
raw = rawData[len(rawData)-1]
}
// Required: true
// Required: false
// AllowEmptyValue: false
if err := validate.RequiredString("state", "query", raw); err != nil {
return err
if raw == "" { // empty values pass all other validations
return nil
}
o.State = raw
o.State = &raw
return nil
}

View File

@ -36,6 +36,53 @@ func (o *OauthAuthenticateOK) WriteResponse(rw http.ResponseWriter, producer run
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

View File

@ -14,7 +14,7 @@ import (
// OauthAuthenticateURL generates an URL for the oauth authenticate operation
type OauthAuthenticateURL struct {
Code string
State string
State *string
_basePath string
// avoid unkeyed usage
@ -55,7 +55,10 @@ func (o *OauthAuthenticateURL) Build() (*url.URL, error) {
qs.Set("code", codeQ)
}
stateQ := o.State
var stateQ string
if o.State != nil {
stateQ = *o.State
}
if stateQ != "" {
qs.Set("state", stateQ)
}

View File

@ -556,27 +556,6 @@ paths:
500:
description: internal server error
/oauth/authorize:
get:
tags:
- share
operationId: oauthAuthenticate
parameters:
- name: state
in: query
type: string
required: true
- name: code
in: query
type: string
required: true
responses:
200:
description: testing
500:
description: internal server error
/share:
post:
tags:
@ -1003,11 +982,14 @@ definitions:
$ref: "#/definitions/authUser"
oauthProvider:
type: string
enum: [amazon,google]
enum: [github,google]
oauthEmailDomains:
type: array
items:
type: string
oauthAuthorizationCheckInterval:
type: string
reserved:
type: boolean

View File

@ -18,14 +18,16 @@ export function access(options) {
}
/**
* @param {string} state
* @param {string} code
* @param {object} options Optional options
* @param {string} [options.state]
* @return {Promise<object>} testing
*/
export function oauthAuthenticate(state, code) {
export function oauthAuthenticate(code, options) {
if (!options) options = {}
const parameters = {
query: {
state,
state: options.state,
code
}
}

View File

@ -255,6 +255,7 @@
* @property {module:types.authUser[]} authUsers
* @property {string} oauthProvider
* @property {string[]} oauthEmailDomains
* @property {string} oauthAuthorizationCheckInterval
* @property {boolean} reserved
*/