func (c *clientResource) create(w http.ResponseWriter, r *http.Request) { ct := r.Header.Get("content-type") if ct != "application/json" { log.Debugf("Unsupported request content-type: %v", ct) writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "unsupported content-type")) return } var sc schema.Client dec := json.NewDecoder(r.Body) err := dec.Decode(&sc) if err != nil { log.Debugf("Error decoding request body: %v", err) writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "unable to decode request body")) return } ci, err := schema.MapSchemaClientToClientIdentity(sc) if err != nil { log.Debugf("Invalid request data: %v", err) writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidClientMetadata, "missing or invalid field: redirectURIs")) return } if err := ci.Metadata.Valid(); err != nil { log.Debugf("ClientMetadata invalid: %v", err) writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidClientMetadata, err.Error())) return } clientID, err := oidc.GenClientID(ci.Metadata.RedirectURIs[0].Host) if err != nil { log.Errorf("Failed generating ID for new client: %v", err) writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "unable to generate client ID")) return } creds, err := c.repo.New(clientID, ci.Metadata) if err != nil { log.Errorf("Failed creating client: %v", err) writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "unable to create client")) return } ci.Credentials = *creds ssc := schema.MapClientIdentityToSchemaClientWithSecret(ci) w.Header().Add("Location", phttp.NewResourceLocation(r.URL, ci.Credentials.ID)) writeResponseWithBody(w, http.StatusCreated, ssc) }
func (m *LDAPIdentityProvider) LDAPConnect() (*ldap.Conn, error) { var err error var ldapConn *ldap.Conn log.Debugf("LDAPConnect()") if m.useSSL { ldapConn, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", m.serverHost, m.serverPort), m.tlsConfig) if err != nil { return nil, err } } else { ldapConn, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", m.serverHost, m.serverPort)) if err != nil { return nil, err } if m.useTLS { err = ldapConn.StartTLS(m.tlsConfig) if err != nil { return nil, err } } } return ldapConn, err }
func (p *LDAPPool) ldapConnect() (*ldap.Conn, error) { var err error var ldapConn *ldap.Conn log.Debugf("LDAPConnect()") if p.UseSSL { ldapConn, err = ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", p.ServerHost, p.ServerPort), p.TLSConfig) if err != nil { return nil, err } } else { ldapConn, err = ldap.Dial("tcp", fmt.Sprintf("%s:%d", p.ServerHost, p.ServerPort)) if err != nil { return nil, err } if p.UseTLS { err = ldapConn.StartTLS(p.TLSConfig) if err != nil { return nil, err } } } return ldapConn, err }
func (n *pcsStepNext) step(fn pcsStepFunc) (next pcsStepper) { ttl, err := fn() if err == nil { next = &pcsStepNext{aft: ttl} log.Debugf("Refreshed jwt, next attempt in %v", next.after()) } else { next = &pcsStepRetry{aft: time.Second} log.Errorf("JWT refresh failed, retrying in %v: %v", next.after(), err) } return }
func (s *grpcServer) authToken(jwt jose.JWT) (string, *oidc.ClientMetadata, error) { ciRepo := s.server.ClientIdentityRepo keys, err := s.server.KeyManager.PublicKeys() if err != nil { log.Errorf("grpc.go: Failed to get keys: %v", err) return "", nil, errors.New("errorAccessDenied") } if len(keys) == 0 { log.Error("grpc.go: No keys available for verification client") return "", nil, errors.New("errorAccessDenied") } ok, err := oidc.VerifySignature(jwt, keys) if err != nil { log.Errorf("grpc.go: Failed to verify signature: %v", err) return "", nil, err } if !ok { log.Info("grpc.go: token signature is not verified") return "", nil, errors.New("invalid token") } clientID, err := oidc.VerifyClientClaims(jwt, s.server.IssuerURL.String()) if err != nil { log.Errorf("grpc.go: Failed to verify JWT claims: %v", err) return "", nil, errors.New("failed to verify jwt claims token") } md, err := ciRepo.Metadata(clientID) if md == nil || err != nil { log.Errorf("grpc.go: Failed to find clientID: %s, error=%v", clientID, err) return "", nil, err } //client must be admin in order to use login and register grpc apis. ok, err = ciRepo.IsDexAdmin(clientID) if err != nil { return "", nil, err } if !ok { log.Infof("grpc.go: Client [%s] is not admin", clientID) return "", nil, errors.New("errorAccessDenied") } log.Debugf("grpc.go: Authenticated token for client ID %s", clientID) return clientID, md, nil }
func main() { fs := flag.NewFlagSet("dex-worker", flag.ExitOnError) listen := fs.String("listen", "http://127.0.0.1:5556", "the address that the server will listen on") issuer := fs.String("issuer", "http://127.0.0.1:5556", "the issuer's location") certFile := fs.String("tls-cert-file", "", "the server's certificate file for TLS connection") keyFile := fs.String("tls-key-file", "", "the server's private key file for TLS connection") templates := fs.String("html-assets", "./static/html", "directory of html template files") emailTemplateDirs := flagutil.StringSliceFlag{"./static/email"} fs.Var(&emailTemplateDirs, "email-templates", "comma separated list of directories of email template files") emailFrom := fs.String("email-from", "", "emails sent from dex will come from this address") emailConfig := fs.String("email-cfg", "./static/fixtures/emailer.json", "configures emailer.") enableRegistration := fs.Bool("enable-registration", false, "Allows users to self-register") noDB := fs.Bool("no-db", false, "manage entities in-process w/o any encryption, used only for single-node testing") // UI-related: issuerName := fs.String("issuer-name", "dex", "The name of this dex installation; will appear on most pages.") issuerLogoURL := fs.String("issuer-logo-url", "https://coreos.com/assets/images/brand/coreos-wordmark-135x40px.png", "URL of an image representing the issuer") // ignored if --no-db is set dbURL := fs.String("db-url", "", "DSN-formatted database connection string") keySecrets := pflag.NewBase64List(32) fs.Var(keySecrets, "key-secrets", "A comma-separated list of base64 encoded 32 byte strings used as symmetric keys used to encrypt/decrypt signing key data in DB. The first key is considered the active key and used for encryption, while the others are used to decrypt.") useOldFormat := fs.Bool("use-deprecated-secret-format", false, "In prior releases, the database used AES-CBC to encrypt keys. New deployments should use the default AES-GCM encryption.") dbMaxIdleConns := fs.Int("db-max-idle-conns", 0, "maximum number of connections in the idle connection pool") dbMaxOpenConns := fs.Int("db-max-open-conns", 0, "maximum number of open connections to the database") // used only if --no-db is set connectors := fs.String("connectors", "./static/fixtures/connectors.json", "JSON file containg set of IDPC configs") clients := fs.String("clients", "./static/fixtures/clients.json", "json file containing set of clients") users := fs.String("users", "./static/fixtures/users.json", "json file containing set of users") logDebug := fs.Bool("log-debug", false, "log debug-level information") logTimestamps := fs.Bool("log-timestamps", false, "prefix log lines with timestamps") if err := fs.Parse(os.Args[1:]); err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } if err := pflag.SetFlagsFromEnv(fs, "DEX_WORKER"); err != nil { fmt.Fprintln(os.Stderr, err.Error()) os.Exit(1) } if *logDebug { log.EnableDebug() log.Infof("Debug logging enabled.") log.Debugf("Debug logging enabled.") } if *logTimestamps { log.EnableTimestamps() } // Validate listen address. lu, err := url.Parse(*listen) if err != nil { log.Fatalf("Invalid listen address %q: %v", *listen, err) } switch lu.Scheme { case "http": case "https": if *certFile == "" || *keyFile == "" { log.Fatalf("Must provide certificate file and private key file") } default: log.Fatalf("Only 'http' and 'https' schemes are supported") } // Validate issuer address. iu, err := url.Parse(*issuer) if err != nil { log.Fatalf("Invalid issuer URL %q: %v", *issuer, err) } if iu.Scheme != "http" && iu.Scheme != "https" { log.Fatalf("Only 'http' and 'https' schemes are supported") } scfg := server.ServerConfig{ IssuerURL: *issuer, TemplateDir: *templates, EmailTemplateDirs: emailTemplateDirs, EmailFromAddress: *emailFrom, EmailerConfigFile: *emailConfig, IssuerName: *issuerName, IssuerLogoURL: *issuerLogoURL, EnableRegistration: *enableRegistration, } if *noDB { log.Warning("Running in-process without external database or key rotation") scfg.StateConfig = &server.SingleServerConfig{ ClientsFile: *clients, ConnectorsFile: *connectors, UsersFile: *users, } } else { if len(keySecrets.BytesSlice()) == 0 { log.Fatalf("Must specify at least one key secret") } if *dbMaxIdleConns == 0 { log.Warning("Running with no limit on: database idle connections") } if *dbMaxOpenConns == 0 { log.Warning("Running with no limit on: database open connections") } dbCfg := db.Config{ DSN: *dbURL, MaxIdleConnections: *dbMaxIdleConns, MaxOpenConnections: *dbMaxOpenConns, } scfg.StateConfig = &server.MultiServerConfig{ KeySecrets: keySecrets.BytesSlice(), DatabaseConfig: dbCfg, UseOldFormat: *useOldFormat, } } srv, err := scfg.Server() if err != nil { log.Fatalf("Unable to build Server: %v", err) } var cfgs []connector.ConnectorConfig var sleep time.Duration for { var err error cfgs, err = srv.ConnectorConfigRepo.All() if len(cfgs) > 0 && err == nil { break } sleep = ptime.ExpBackoff(sleep, 8*time.Second) if err != nil { log.Errorf("Unable to load connectors, retrying in %v: %v", sleep, err) } else { log.Errorf("No connectors, will wait. Retrying in %v.", sleep) } time.Sleep(sleep) } for _, cfg := range cfgs { cfg := cfg if err = srv.AddConnector(cfg); err != nil { log.Fatalf("Failed registering connector: %v", err) } } h := srv.HTTPHandler() h = handlers.LoggingHandler(log.InfoWriter(), h) httpsrv := &http.Server{ Addr: lu.Host, Handler: h, } log.Infof("Binding to %s...", httpsrv.Addr) go func() { if lu.Scheme == "http" { log.Fatal(httpsrv.ListenAndServe()) } else { log.Fatal(httpsrv.ListenAndServeTLS(*certFile, *keyFile)) } }() <-srv.Run() }
func (h *InvitationHandler) handleGET(w http.ResponseWriter, r *http.Request) { q := r.URL.Query() token := q.Get("token") keys, err := h.keysFunc() if err != nil { log.Errorf("internal error getting public keys: %v", err) writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "There's been an error processing your request.")) return } invite, err := user.ParseAndVerifyInvitationToken(token, h.issuerURL, keys) if err != nil { log.Debugf("invalid invitation token: %v (%v)", err, token) writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "Your invitation could not be verified")) return } _, err = h.um.VerifyEmail(invite) if err != nil && err != manager.ErrorEmailAlreadyVerified { // Allow AlreadyVerified folks to pass through- otherwise // folks who encounter an error after passing this point will // never be able to set their passwords. log.Debugf("error attempting to verify email: %v", err) switch err { case user.ErrorNotFound: writeAPIError(w, http.StatusBadRequest, newAPIError(errorInvalidRequest, "Your email does not match the email address on file")) return default: log.Errorf("internal error verifying email: %v", err) writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "There's been an error processing your request.")) return } } passwordReset := invite.PasswordReset(h.issuerURL, h.redirectValidityWindow) signer, err := h.signerFunc() if err != nil || signer == nil { log.Errorf("error getting signer: %v (signer: %v)", err, signer) writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "There's been an error processing your request.")) return } jwt, err := jose.NewSignedJWT(passwordReset.Claims, signer) if err != nil { log.Errorf("error constructing or signing PasswordReset from Invitation JWT: %v", err) writeAPIError(w, http.StatusInternalServerError, newAPIError(errorServerError, "There's been an error processing your request.")) return } passwordResetToken := jwt.Encode() passwordResetURL := h.passwordResetURL newQuery := passwordResetURL.Query() newQuery.Set("token", passwordResetToken) passwordResetURL.RawQuery = newQuery.Encode() http.Redirect(w, r, passwordResetURL.String(), http.StatusSeeOther) }