コード例 #1
0
ファイル: update.go プロジェクト: otsimo/accounts
func handleChangePasswordFunc(o *OtsimoAccounts) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			r.ParseForm()
			oldPass := r.PostFormValue("old_password")
			newPass := r.PostFormValue("new_password")
			userID := r.PostFormValue("user_id")

			if userID == "" || oldPass == "" || newPass == "" {
				log.Infof("update.go: invalid body '%s'", userID)
				writeError(w, http.StatusBadRequest, "invalid body")
				return
			}
			resp, err := o.Dex.ChangePassword(context.Background(), &pb.ChangePasswordRequest{
				UserId:      userID,
				OldPassword: oldPass,
				NewPassword: newPass,
			})

			log.Infof("update.go: change password result is %q %v", resp, err)
			if err != nil {
				writeError(w, http.StatusInternalServerError, err.Error())
				return
			}
			writeResponseWithBody(w, http.StatusOK, resp)
		} else {
			writeError(w, http.StatusNotFound, "Not Found")
		}
	}
}
コード例 #2
0
ファイル: update.go プロジェクト: otsimo/accounts
func handleChangeEmailFunc(o *OtsimoAccounts) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			r.ParseForm()
			oldEmail := r.PostFormValue("old_email")
			newEmail := r.PostFormValue("new_email")

			if oldEmail == "" || newEmail == "" || oldEmail == newEmail {
				log.Info("update.go: invalid body")
				writeError(w, http.StatusBadRequest, "invalid body")
				return
			}
			resp, err := o.Dex.ChangeEmail(context.Background(), &pb.ChangeEmailRequest{
				OldEmail: oldEmail,
				NewEmail: newEmail,
			})

			log.Infof("update.go: change email result is %q %v", resp, err)
			if err != nil {
				writeError(w, http.StatusInternalServerError, err.Error())
				return
			}
			id := r.Header.Get("sub")
			if bson.IsObjectIdHex(id) {
				o.Api.UpdateProfile(context.Background(), &apipb.Profile{Id: bson.ObjectIdHex(id), Email: newEmail})
			}

			writeResponseWithBody(w, http.StatusOK, resp)
		} else {
			writeError(w, http.StatusNotFound, "Not Found")
		}
	}
}
コード例 #3
0
ファイル: register.go プロジェクト: otsimo/accounts
func registerUser(o *OtsimoAccounts, email, password, firstName, lastName, language string) (*pb.RegisterResponse, error) {
	resp, err := o.Dex.Register(context.Background(), &pb.RegisterRequest{
		Email:       email,
		DisplayName: fmt.Sprintf("%s %s", firstName, lastName),
		Password:    password,
	})

	log.Infof("register.go: register result of '%s' is %q %v", email, resp, err)
	if err != nil {
		return nil, err
	}
	_, errapi := o.Api.AddProfile(context.Background(), &apipb.Profile{
		Id:        bson.ObjectIdHex(resp.UserId),
		Email:     email,
		FirstName: firstName,
		LastName:  lastName,
		Language:  language,
	})
	if errapi != nil {
		//Disable or delete user
		log.Errorf("register.go: failed to add profile %+v", errapi)
		_, err = o.Dex.RemoveUser(context.Background(), &pb.RemoveRequest{Id: resp.UserId, Email: email})
		if err != nil {
			log.Errorf("register.go: Holly F**K!!: failed to add profile and remove user [error]=%+v [user_id]='%s' [user_email]='%s'", err, resp.UserId, email)
			return nil, err
		}
		return nil, fmt.Errorf("failed to register user, adding to api service failed:%v", errapi)
	}
	return resp, nil
}
コード例 #4
0
ファイル: server.go プロジェクト: derekparker/dex
func (s *Server) ClientCredsToken(creds oidc.ClientCredentials) (*jose.JWT, error) {
	ok, err := s.ClientIdentityRepo.Authenticate(creds)
	if err != nil {
		log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err)
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}
	if !ok {
		return nil, oauth2.NewError(oauth2.ErrorInvalidClient)
	}

	signer, err := s.KeyManager.Signer()
	if err != nil {
		log.Errorf("Failed to generate ID token: %v", err)
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}

	now := time.Now()
	exp := now.Add(s.SessionManager.ValidityWindow)
	claims := oidc.NewClaims(s.IssuerURL.String(), creds.ID, creds.ID, now, exp)
	claims.Add("name", creds.ID)

	jwt, err := jose.NewSignedJWT(claims, signer)
	if err != nil {
		log.Errorf("Failed to generate ID token: %v", err)
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}

	log.Infof("Client token sent: clientID=%s", creds.ID)

	return jwt, nil
}
コード例 #5
0
ファイル: gc.go プロジェクト: jbagel2/dex
func (gc *GarbageCollector) Run() chan struct{} {
	stop := make(chan struct{})

	go func() {
		var failing bool
		next := gc.interval
		for {
			select {
			case <-gc.clock.After(next):
				if anyPurgeErrors(purgeAll(gc.purgers)) {
					if !failing {
						failing = true
						next = time.Second
					} else {
						next = ptime.ExpBackoff(next, time.Minute)
					}
					log.Errorf("Failed garbage collection, retrying in %v", next)
				} else {
					failing = false
					next = gc.interval
					log.Infof("Garbage collection complete, running again in %v", next)
				}
			case <-stop:
				return
			}
		}
	}()

	return stop
}
コード例 #6
0
ファイル: server.go プロジェクト: GamerockSA/dex
func (s *Server) NewSession(ipdcID, clientID, clientState string, redirectURL url.URL, nonce string, register bool, scope []string) (string, error) {
	sessionID, err := s.SessionManager.NewSession(ipdcID, clientID, clientState, redirectURL, nonce, register, scope)
	if err != nil {
		return "", err
	}

	log.Infof("Session %s created: clientID=%s clientState=%s", sessionID, clientID, clientState)
	return s.SessionManager.NewSessionKey(sessionID)
}
コード例 #7
0
ファイル: oidc.go プロジェクト: otsimo/accounts
func (r *pcsStepRetry) step(fn pcsStepFunc) (next pcsStepper) {
	ttl, err := fn()
	if err == nil {
		next = &pcsStepNext{aft: ttl}
		log.Infof("JWT refresh no longer failing")
	} else {
		next = &pcsStepRetry{aft: timeutil.ExpBackoff(r.aft, time.Minute)}
		log.Errorf("JWT refresh still failing, retrying in %v: %v", next.after(), err)
	}
	return
}
コード例 #8
0
ファイル: server.go プロジェクト: derekparker/dex
func (s *Server) RefreshToken(creds oidc.ClientCredentials, token string) (*jose.JWT, error) {
	ok, err := s.ClientIdentityRepo.Authenticate(creds)
	if err != nil {
		log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err)
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}
	if !ok {
		log.Errorf("Failed to Authenticate client %s", creds.ID)
		return nil, oauth2.NewError(oauth2.ErrorInvalidClient)
	}

	userID, err := s.RefreshTokenRepo.Verify(creds.ID, token)
	switch err {
	case nil:
		break
	case refresh.ErrorInvalidToken:
		return nil, oauth2.NewError(oauth2.ErrorInvalidRequest)
	case refresh.ErrorInvalidClientID:
		return nil, oauth2.NewError(oauth2.ErrorInvalidClient)
	default:
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}

	user, err := s.UserRepo.Get(nil, userID)
	if err != nil {
		// The error can be user.ErrorNotFound, but we are not deleting
		// user at this moment, so this shouldn't happen.
		log.Errorf("Failed to fetch user %q from repo: %v: ", userID, err)
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}

	signer, err := s.KeyManager.Signer()
	if err != nil {
		log.Errorf("Failed to refresh ID token: %v", err)
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}

	now := time.Now()
	expireAt := now.Add(session.DefaultSessionValidityWindow)

	claims := oidc.NewClaims(s.IssuerURL.String(), user.ID, creds.ID, now, expireAt)
	user.AddToClaims(claims)

	jwt, err := jose.NewSignedJWT(claims, signer)
	if err != nil {
		log.Errorf("Failed to generate ID token: %v", err)
		return nil, oauth2.NewError(oauth2.ErrorServerError)
	}

	log.Infof("New token sent: clientID=%s", creds.ID)

	return jwt, nil
}
コード例 #9
0
ファイル: server.go プロジェクト: derekparker/dex
func (s *Server) AddConnector(cfg connector.ConnectorConfig) error {
	connectorID := cfg.ConnectorID()
	ns := s.IssuerURL
	ns.Path = path.Join(ns.Path, httpPathAuth, connectorID)

	idpc, err := cfg.Connector(ns, s.Login, s.Templates)
	if err != nil {
		return err
	}

	s.Connectors = append(s.Connectors, idpc)

	sortable := sortableIDPCs(s.Connectors)
	sort.Sort(sortable)

	// We handle the LocalConnector specially because it needs access to the
	// UserRepo and the PasswordInfoRepo; if it turns out that other connectors
	// need access to these resources we'll figure out how to provide it in a
	// cleaner manner.
	localConn, ok := idpc.(*connector.LocalConnector)
	if ok {
		s.localConnectorID = connectorID
		if s.UserRepo == nil {
			return errors.New("UserRepo cannot be nil")
		}

		if s.PasswordInfoRepo == nil {
			return errors.New("PasswordInfoRepo cannot be nil")
		}

		localConn.SetLocalIdentityProvider(&connector.LocalIdentityProvider{
			UserRepo:         s.UserRepo,
			PasswordInfoRepo: s.PasswordInfoRepo,
		})

		localCfg, ok := cfg.(*connector.LocalConnectorConfig)
		if !ok {
			return errors.New("config for LocalConnector not a LocalConnectorConfig?")
		}

		if len(localCfg.PasswordInfos) > 0 {
			err := user.LoadPasswordInfos(s.PasswordInfoRepo,
				localCfg.PasswordInfos)
			if err != nil {
				return err
			}
		}
	}

	log.Infof("Loaded IdP connector: id=%s type=%s", connectorID, cfg.ConnectorType())
	return nil
}
コード例 #10
0
ファイル: mailgun.go プロジェクト: Tecsisa/dex
func (m *mailgunEmailer) SendMail(subject, text, html string, to ...string) error {
	msg := m.mg.NewMessage(m.from, subject, text, to...)
	if html != "" {
		msg.SetHtml(html)
	}
	mes, id, err := m.mg.Send(msg)
	if err != nil {
		counterEmailSendErr.Add(1)
		return err
	}
	log.Infof("SendMail: msgID: %v: %q", id, mes)
	return nil
}
コード例 #11
0
ファイル: api.go プロジェクト: adrianlop/dex
func (u *UsersAPI) DisableUser(creds Creds, userID string, disable bool) (schema.UserDisableResponse, error) {
	log.Infof("userAPI: DisableUser")
	if !u.Authorize(creds) {
		return schema.UserDisableResponse{}, ErrorUnauthorized
	}

	if err := u.manager.Disable(userID, disable); err != nil {
		return schema.UserDisableResponse{}, mapError(err)
	}

	return schema.UserDisableResponse{
		Ok: true,
	}, nil
}
コード例 #12
0
ファイル: api.go プロジェクト: Tecsisa/dex
func (u *UsersAPI) GetUser(creds Creds, id string) (schema.User, error) {
	log.Infof("userAPI: GetUser")
	if !u.Authorize(creds) {
		return schema.User{}, ErrorUnauthorized
	}

	usr, err := u.userManager.Get(id)

	if err != nil {
		return schema.User{}, mapError(err)
	}

	return userToSchemaUser(usr), nil
}
コード例 #13
0
ファイル: api.go プロジェクト: adrianlop/dex
func (u *UsersAPI) CreateUser(creds Creds, usr schema.User, redirURL url.URL) (schema.UserCreateResponse, error) {
	log.Infof("userAPI: CreateUser")
	if !u.Authorize(creds) {
		return schema.UserCreateResponse{}, ErrorUnauthorized
	}

	hash, err := generateTempHash()
	if err != nil {
		return schema.UserCreateResponse{}, mapError(err)
	}

	metadata, err := u.clientIdentityRepo.Metadata(creds.ClientID)
	if err != nil {
		return schema.UserCreateResponse{}, mapError(err)
	}

	validRedirURL, err := client.ValidRedirectURL(&redirURL, metadata.RedirectURLs)
	if err != nil {
		return schema.UserCreateResponse{}, ErrorInvalidRedirectURL
	}

	id, err := u.manager.CreateUser(schemaUserToUser(usr), user.Password(hash), u.localConnectorID)
	if err != nil {
		return schema.UserCreateResponse{}, mapError(err)
	}

	userUser, err := u.manager.Get(id)
	if err != nil {
		return schema.UserCreateResponse{}, mapError(err)
	}

	usr = userToSchemaUser(userUser)

	url, err := u.emailer.SendInviteEmail(usr.Email, validRedirURL, creds.ClientID)

	// An email is sent only if we don't get a link and there's no error.
	emailSent := err == nil && url == nil

	var resetLink string
	if url != nil {
		resetLink = url.String()
	}

	return schema.UserCreateResponse{
		User:              &usr,
		EmailSent:         emailSent,
		ResetPasswordLink: resetLink,
	}, nil
}
コード例 #14
0
ファイル: register.go プロジェクト: otsimo/accounts
func handleRegisterFunc(o *OtsimoAccounts) http.HandlerFunc {
	handlePOST := func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		email := r.PostFormValue("username")
		password := r.PostFormValue("password")
		firstName := r.PostFormValue("first_name")
		lastName := r.PostFormValue("last_name")
		language := r.PostFormValue("language")
		if email == "" || password == "" {
			writeError(w, http.StatusBadRequest, "invalid body")
			return
		}
		result, err := registerUser(o, email, password, firstName, lastName, language)
		if err != nil {
			writeError(w, http.StatusInternalServerError, err.Error())
			return
		}
		writeResponseWithBody(w, http.StatusOK, result.Token)
	}
	handleGET := func(w http.ResponseWriter, r *http.Request) {
		oac, err := o.Oidc.OAuthClient()
		if err != nil {
			log.Errorf("failed to create OAuthClient %v", err)
			writeError(w, http.StatusInternalServerError, err.Error())
		}

		u, err := url.Parse(oac.AuthCodeURL("", "", ""))
		q := u.Query()
		q.Set("register", "1")
		if err != nil {
			log.Errorf("failed to create authCoreURL %v", err)
			writeError(w, http.StatusInternalServerError, err.Error())
		}
		u.RawQuery = q.Encode()
		log.Infof("URL: %v", u.String())
		http.Redirect(w, r, u.String(), http.StatusFound)
	}
	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			handlePOST(w, r)
		} else if r.Method == "GET" {
			handleGET(w, r)
		} else {
			writeError(w, http.StatusNotFound, "wrong Http Verb")
		}
	}
}
コード例 #15
0
ファイル: grpc.go プロジェクト: otsimo/accounts
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
}
コード例 #16
0
ファイル: login.go プロジェクト: otsimo/accounts
func handleLoginFunc(o *OtsimoAccounts) http.HandlerFunc {
	handlePOST := func(w http.ResponseWriter, r *http.Request) {
		r.ParseForm()
		username := r.PostFormValue("username")
		password := r.PostFormValue("password")
		grant_type := r.PostFormValue("grant_type")

		if username == "" || password == "" || grant_type == "" {
			writeError(w, http.StatusBadRequest, "invalid body")
			return
		}

		resp, err := o.Dex.Login(context.Background(), &pb.LoginRequest{
			GrantType: grant_type,
			BasicAuth: "Basic " + basicAuth(username, password),
		})
		log.Infof("login.go: login result of '%s' is %q %v", username, resp, err)
		if err != nil {
			writeError(w, http.StatusInternalServerError, err.Error())
			return
		}
		writeResponseWithBody(w, http.StatusOK, resp)
	}
	handleGET := func(w http.ResponseWriter, r *http.Request) {
		oac, err := o.Oidc.OAuthClient()
		if err != nil {
			writeError(w, http.StatusInternalServerError, "unabled create oauth client")
			return
		}
		u, err := url.Parse(oac.AuthCodeURL("", "", ""))
		if err != nil {
			writeError(w, http.StatusInternalServerError, "unabled create auth code url")
			return
		}
		http.Redirect(w, r, u.String(), http.StatusFound)
	}

	return func(w http.ResponseWriter, r *http.Request) {
		if r.Method == "POST" {
			handlePOST(w, r)
		} else if r.Method == "GET" {
			handleGET(w, r)
		} else {
			writeError(w, http.StatusNotFound, "wrong HTTP Verb")
		}
	}
}
コード例 #17
0
ファイル: main.go プロジェクト: GamerockSA/dex
func handleRegisterFunc(c *oidc.Client) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		oac, err := c.OAuthClient()
		if err != nil {
			panic("unable to proceed")
		}

		u, err := url.Parse(oac.AuthCodeURL("", "", ""))
		q := u.Query()
		q.Set("register", "1")
		if err != nil {
			panic("unable to proceed")
		}
		u.RawQuery = q.Encode()
		log.Infof("URL: %v", u.String())
		http.Redirect(w, r, u.String(), http.StatusFound)
	}
}
コード例 #18
0
ファイル: api.go プロジェクト: ryanj/dex
func (u *UsersAPI) ResendEmailInvitation(creds Creds, userID string, redirURL url.URL) (schema.ResendEmailInvitationResponse, error) {
	log.Infof("userAPI: ResendEmailInvitation")
	if !u.Authorize(creds) {
		return schema.ResendEmailInvitationResponse{}, ErrorUnauthorized
	}

	metadata, err := u.clientIdentityRepo.Metadata(creds.ClientID)
	if err != nil {
		return schema.ResendEmailInvitationResponse{}, mapError(err)
	}

	validRedirURL, err := client.ValidRedirectURL(&redirURL, metadata.RedirectURIs)
	if err != nil {
		return schema.ResendEmailInvitationResponse{}, ErrorInvalidRedirectURL
	}

	// Retrieve user to check if it's already created
	userUser, err := u.manager.Get(userID)
	if err != nil {
		return schema.ResendEmailInvitationResponse{}, mapError(err)
	}

	// Check if email is verified
	if userUser.EmailVerified {
		return schema.ResendEmailInvitationResponse{}, ErrorVerifiedEmail
	}

	url, err := u.emailer.SendInviteEmail(userUser.Email, validRedirURL, creds.ClientID)

	// An email is sent only if we don't get a link and there's no error.
	emailSent := err == nil && url == nil

	// If email is not sent a reset link will be generated
	var resetLink string
	if url != nil {
		resetLink = url.String()
	}

	return schema.ResendEmailInvitationResponse{
		EmailSent:         emailSent,
		ResetPasswordLink: resetLink,
	}, nil
}
コード例 #19
0
ファイル: oidc.go プロジェクト: otsimo/accounts
func NewClient(clientID, clientSecret, discovery, redirectURL string, tlsConfig *tls.Config) (*Client, *ClientCredsTokenManager) {
	cc := oidc.ClientCredentials{
		ID:     clientID,
		Secret: clientSecret,
	}
	httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}

	var cfg oidc.ProviderConfig
	var err error

	for {
		cfg, err = oidc.FetchProviderConfig(httpClient, discovery)
		if err == nil {
			break
		}

		sleep := 3 * time.Second
		log.Errorf("Failed fetching provider config, trying again in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	log.Infof("Fetched provider config from %s", discovery)

	ccfg := oidc.ClientConfig{
		HTTPClient:     httpClient,
		ProviderConfig: cfg,
		Credentials:    cc,
		RedirectURL:    redirectURL,
	}

	client, err := NewOIDCClient(ccfg)
	if err != nil {
		log.Fatalf("Unable to create Client: %v", err)
	}

	client.SyncProviderConfig(discovery)

	tm := NewClientCredsTokenManager(client, discovery)
	tm.Run()
	tm.WaitUntilInitialSync()

	return client, tm
}
コード例 #20
0
ファイル: session_key.go プロジェクト: Tecsisa/dex
func (r *SessionKeyRepo) purge() error {
	qt := r.quote(sessionKeyTableName)
	q := fmt.Sprintf("DELETE FROM %s WHERE stale = $1 OR expires_at < $2", qt)
	res, err := r.executor(nil).Exec(q, true, r.clock.Now().Unix())
	if err != nil {
		return err
	}

	d := "unknown # of"
	if n, err := res.RowsAffected(); err == nil {
		if n == 0 {
			return nil
		}
		d = fmt.Sprintf("%d", n)
	}

	log.Infof("Deleted %s stale row(s) from %s table", d, sessionKeyTableName)
	return nil
}
コード例 #21
0
ファイル: session.go プロジェクト: adrianlop/dex
func (r *SessionRepo) purge() error {
	qt := pq.QuoteIdentifier(sessionTableName)
	q := fmt.Sprintf("DELETE FROM %s WHERE expires_at < $1 OR state = $2", qt)
	res, err := r.dbMap.Exec(q, r.clock.Now().Unix(), string(session.SessionStateDead))
	if err != nil {
		return err
	}

	d := "unknown # of"
	if n, err := res.RowsAffected(); err == nil {
		if n == 0 {
			return nil
		}
		d = fmt.Sprintf("%d", n)
	}

	log.Infof("Deleted %s stale row(s) from %s table", d, sessionTableName)
	return nil
}
コード例 #22
0
ファイル: connector_ldap.go プロジェクト: cgenuity/dex
func (c *LDAPConnector) Sync() chan struct{} {
	stop := make(chan struct{})

	go func() {
		for {
			select {
			case <-time.After(c.idp.ldapPool.PoolCheckTimer):
				alive, killed := c.idp.ldapPool.CheckConnections()
				if alive > 0 {
					log.Infof("Connector ID=%v idle_conns=%v", c.id, alive)
				}
				if killed > 0 {
					log.Warningf("Connector ID=%v closed %v dead connections.", c.id, killed)
				}
			case <-stop:
				return
			}
		}
	}()
	return stop
}
コード例 #23
0
ファイル: email.go プロジェクト: otsimo/accounts
func (m *otsimoEmailer) SendMail(from, subject, event, data string, to ...string) error {
	email := &pb.Email{
		FromEmail: from,
		Subject:   subject,
		DataJson:  data,
		ToEmail:   to,
	}
	mes := &pb.Message{
		Event:   event,
		Targets: pb.NewTargets(pb.NewEmailTarget(email)),
	}
	if m.fake {
		log.Infof("email.go: email sent: %v", mes)
		return nil
	}
	_, err := m.client.SendMessage(context.Background(), mes)
	if err != nil {
		log.Errorf("email.go: sending email error: %v", err)
	}
	return err
}
コード例 #24
0
ファイル: grpc.go プロジェクト: otsimo/accounts
func (g *grpcServer) Register(ctx context.Context, in *pb.RegisterRequest) (*pb.RegisterResponse, error) {
	jwtClient, err := getJWTToken(ctx)
	if err != nil {
		log.Errorf("grpc.go: getJWTToken error %v", err)
		return nil, err
	}
	clientId, md, err := g.authToken(jwtClient)
	if err != nil {
		log.Errorf("grpc.go: authToken failed error=%v", err)
		return nil, err
	}

	id, err := registerFromLocalConnector(g.server.UserManager, g.localConnectorID, in.Email, in.Password)
	if err != nil {
		return nil, err
	}

	//send email
	g.server.UserEmailer.SendEmailVerification(id, clientId, md.RedirectURIs[0])

	now := time.Now()
	jwt, rt, err := g.Token(id, clientId, now, now.Add(session.DefaultSessionValidityWindow))

	if err != nil {
		return nil, err
	}

	log.Infof("grpc.go: token sent: userID=%s email:%s", id, in.Email)

	return &pb.RegisterResponse{
		UserId: id,
		Token: &pb.Token{
			AccessToken:  jwt.Encode(),
			TokenType:    "bearer",
			RefreshToken: rt,
		},
	}, nil
}
コード例 #25
0
ファイル: grpc.go プロジェクト: otsimo/accounts
func (g *grpcServer) Login(ctx context.Context, in *pb.LoginRequest) (*pb.Token, error) {
	jwtClient, err := getJWTToken(ctx)
	if err != nil {
		log.Errorf("grpc.go: getJWTToken error %v", err)
		return nil, err
	}
	clientId, _, err := g.authToken(jwtClient)
	if err != nil {
		log.Errorf("grpc.go: authToken failed error=%v", err)
		return nil, err
	}

	email, password, ok := parseBasicAuth(in.BasicAuth)
	if !ok {
		return nil, errors.New("failed to parse basic auth")
	}

	i, err := g.idp.Identity(email, password)
	if err != nil {
		return nil, err
	}

	now := time.Now()
	jwt, rt, err := g.Token(i.ID, clientId, now, now.Add(session.DefaultSessionValidityWindow))

	if err != nil {
		return nil, err
	}

	log.Infof("grpc.go: token sent: userID=%s email:%s", i.ID, email)

	return &pb.Token{
		AccessToken:  jwt.Encode(),
		TokenType:    "bearer",
		RefreshToken: rt,
	}, nil
}
コード例 #26
0
ファイル: grpc.go プロジェクト: otsimo/accounts
func ServeGrpc(cfg *server.ServerConfig, srv *server.Server, grpcUrl string, certFile, keyFile string, tf repo.TransactionFactory) {
	var opts []grpc.ServerOption
	if certFile != "" && keyFile != "" {
		creds, err := credentials.NewServerTLSFromFile(certFile, keyFile)
		if err != nil {
			log.Fatalf("grpc.go: Failed to generate credentials %v", err)
		}
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	}
	s := grpc.NewServer(opts...)

	rpcSrv := &grpcServer{
		server: srv,
		idp: &connector.LocalIdentityProvider{
			UserRepo:         srv.UserRepo,
			PasswordInfoRepo: srv.PasswordInfoRepo,
		},
		begin: tf,
	}

	for _, c := range srv.Connectors {
		if cc, ok := c.(*connector.LocalConnector); ok {
			rpcSrv.localConnectorID = cc.ID()
			break
		}
	}

	grpclog.SetLogger(golog.New(os.Stdout, "", 0))
	pb.RegisterDexServiceServer(s, rpcSrv)

	lis, err := net.Listen("tcp", grpcUrl)
	if err != nil {
		log.Fatalf("grpc.go: failed to listen: %v", err)
	}
	log.Infof("grpc: Grpc server starting on %s", grpcUrl)
	s.Serve(lis)
}
コード例 #27
0
ファイル: api.go プロジェクト: adrianlop/dex
func (u *UsersAPI) ListUsers(creds Creds, maxResults int, nextPageToken string) ([]*schema.User, string, error) {
	log.Infof("userAPI: ListUsers")

	if !u.Authorize(creds) {
		return nil, "", ErrorUnauthorized
	}

	if maxResults > maxUsersPerPage {
		return nil, "", ErrorMaxResultsTooHigh
	}

	users, tok, err := u.manager.List(user.UserFilter{}, maxResults, nextPageToken)
	if err != nil {
		return nil, "", mapError(err)
	}

	list := []*schema.User{}
	for _, usr := range users {
		schemaUsr := userToSchemaUser(usr)
		list = append(list, &schemaUsr)
	}

	return list, tok, nil
}
コード例 #28
0
ファイル: main.go プロジェクト: GamerockSA/dex
func main() {
	fs := flag.NewFlagSet("oidc-app", flag.ExitOnError)
	listen := fs.String("listen", "http://127.0.0.1:5555", "")
	redirectURL := fs.String("redirect-url", "http://127.0.0.1:5555/callback", "")
	clientID := fs.String("client-id", "example-app", "")
	clientSecret := fs.String("client-secret", "ZXhhbXBsZS1hcHAtc2VjcmV0", "")
	caFile := fs.String("trusted-ca-file", "", "the TLS CA file, if empty then the host's root CA will be used")

	certFile := fs.String("tls-cert-file", "", "the TLS cert file. If empty, the app will listen on HTTP")
	keyFile := fs.String("tls-key-file", "", "the TLS key file. If empty, the app will listen on HTTP")

	discovery := fs.String("discovery", "http://127.0.0.1:5556", "")
	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, "EXAMPLE_APP"); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}

	if *logDebug {
		log.EnableDebug()
	}
	if *logTimestamps {
		log.EnableTimestamps()
	}

	if *clientID == "" {
		log.Fatal("--client-id must be set")
	}

	if *clientSecret == "" {
		log.Fatal("--client-secret must be set")
	}

	l, err := url.Parse(*listen)
	if err != nil {
		log.Fatalf("Unable to use --listen flag: %v", err)
	}

	_, p, err := net.SplitHostPort(l.Host)
	if err != nil {
		log.Fatalf("Unable to parse host from --listen flag: %v", err)
	}

	redirectURLParsed, err := url.Parse(*redirectURL)
	if err != nil {
		log.Fatalf("Unable to parse url from --redirect-url flag: %v", err)
	}

	useTLS := *keyFile != "" && *certFile != ""
	if useTLS && (redirectURLParsed.Scheme != "https" || l.Scheme != "https") {
		log.Fatalf(`TLS Cert File and Key File were provided. Ensure listen and redirect URLs are using the "https://" scheme.`)
	}

	cc := oidc.ClientCredentials{
		ID:     *clientID,
		Secret: *clientSecret,
	}

	var tlsConfig tls.Config
	if *caFile != "" {
		roots := x509.NewCertPool()
		pemBlock, err := ioutil.ReadFile(*caFile)
		if err != nil {
			log.Fatalf("Unable to read ca file: %v", err)
		}
		roots.AppendCertsFromPEM(pemBlock)
		tlsConfig.RootCAs = roots
	}

	httpClient := &http.Client{Transport: &http.Transport{TLSClientConfig: &tlsConfig}}

	var cfg oidc.ProviderConfig
	for {
		cfg, err = oidc.FetchProviderConfig(httpClient, *discovery)
		if err == nil {
			break
		}

		sleep := 3 * time.Second
		log.Errorf("Failed fetching provider config, trying again in %v: %v", sleep, err)
		time.Sleep(sleep)
	}

	log.Infof("Fetched provider config from %s: %#v", *discovery, cfg)

	ccfg := oidc.ClientConfig{
		HTTPClient:     httpClient,
		ProviderConfig: cfg,
		Credentials:    cc,
		RedirectURL:    *redirectURL,
		Scope:          append(oidc.DefaultScope, "offline_access"),
	}

	client, err := oidc.NewClient(ccfg)
	if err != nil {
		log.Fatalf("Unable to create Client: %v", err)
	}

	client.SyncProviderConfig(*discovery)

	hdlr := NewClientHandler(client, *discovery, *redirectURLParsed)
	httpsrv := &http.Server{
		Addr:    fmt.Sprintf(":%s", p),
		Handler: hdlr,
	}

	indexBytes, err := Asset("data/index.html")
	if err != nil {
		log.Fatalf("could not load template: %q", err)
	}

	indexTemplate = template.Must(template.New("root").Parse(string(indexBytes)))

	log.Infof("Binding to %s...", httpsrv.Addr)
	if useTLS {
		log.Info("Key and cert file provided. Using TLS")
		log.Fatal(httpsrv.ListenAndServeTLS(*certFile, *keyFile))
	} else {
		log.Fatal(httpsrv.ListenAndServe())
	}
}
コード例 #29
0
ファイル: server.go プロジェクト: GamerockSA/dex
func (s *Server) CodeToken(creds oidc.ClientCredentials, sessionKey string) (*jose.JWT, string, error) {
	ok, err := s.ClientManager.Authenticate(creds)
	if err != nil {
		log.Errorf("Failed fetching client %s from repo: %v", creds.ID, err)
		return nil, "", oauth2.NewError(oauth2.ErrorServerError)
	}
	if !ok {
		log.Errorf("Failed to Authenticate client %s", creds.ID)
		return nil, "", oauth2.NewError(oauth2.ErrorInvalidClient)
	}

	sessionID, err := s.SessionManager.ExchangeKey(sessionKey)
	if err != nil {
		return nil, "", oauth2.NewError(oauth2.ErrorInvalidGrant)
	}

	ses, err := s.SessionManager.Kill(sessionID)
	if err != nil {
		return nil, "", oauth2.NewError(oauth2.ErrorInvalidRequest)
	}

	if ses.ClientID != creds.ID {
		return nil, "", oauth2.NewError(oauth2.ErrorInvalidGrant)
	}

	signer, err := s.KeyManager.Signer()
	if err != nil {
		log.Errorf("Failed to generate ID token: %v", err)
		return nil, "", oauth2.NewError(oauth2.ErrorServerError)
	}

	user, err := s.UserRepo.Get(nil, ses.UserID)
	if err != nil {
		log.Errorf("Failed to fetch user %q from repo: %v: ", ses.UserID, err)
		return nil, "", oauth2.NewError(oauth2.ErrorServerError)
	}

	claims := ses.Claims(s.IssuerURL.String())
	user.AddToClaims(claims)

	s.addClaimsFromScope(claims, ses.Scope, ses.ClientID)

	jwt, err := jose.NewSignedJWT(claims, signer)
	if err != nil {
		log.Errorf("Failed to generate ID token: %v", err)
		return nil, "", oauth2.NewError(oauth2.ErrorServerError)
	}

	// Generate refresh token when 'scope' contains 'offline_access'.
	var refreshToken string

	for _, scope := range ses.Scope {
		if scope == "offline_access" {
			log.Infof("Session %s requests offline access, will generate refresh token", sessionID)

			refreshToken, err = s.RefreshTokenRepo.Create(ses.UserID, creds.ID, ses.Scope)
			switch err {
			case nil:
				break
			default:
				log.Errorf("Failed to generate refresh token: %v", err)
				return nil, "", oauth2.NewError(oauth2.ErrorServerError)
			}
			break
		}
	}

	log.Infof("Session %s token sent: clientID=%s", sessionID, creds.ID)
	return jwt, refreshToken, nil
}
コード例 #30
0
ファイル: server.go プロジェクト: GamerockSA/dex
func (s *Server) Login(ident oidc.Identity, key string) (string, error) {
	sessionID, err := s.SessionManager.ExchangeKey(key)
	if err != nil {
		return "", err
	}

	ses, err := s.SessionManager.AttachRemoteIdentity(sessionID, ident)
	if err != nil {
		return "", err
	}
	log.Infof("Session %s remote identity attached: clientID=%s identity=%#v", sessionID, ses.ClientID, ident)

	if ses.Register {
		code, err := s.SessionManager.NewSessionKey(sessionID)
		if err != nil {
			return "", err
		}

		ru := s.absURL(httpPathRegister)
		q := ru.Query()
		q.Set("code", code)
		q.Set("state", ses.ClientState)
		ru.RawQuery = q.Encode()
		return ru.String(), nil
	}

	remoteIdentity := user.RemoteIdentity{ConnectorID: ses.ConnectorID, ID: ses.Identity.ID}

	// Get the connector used to log the user in.
	var conn connector.Connector
	for _, c := range s.Connectors {
		if c.ID() == ses.ConnectorID {
			conn = c
			break
		}
	}
	if conn == nil {
		return "", fmt.Errorf("session contained invalid connector ID (%s)", ses.ConnectorID)
	}

	usr, err := s.UserRepo.GetByRemoteIdentity(nil, remoteIdentity)
	if err == user.ErrorNotFound {
		if ses.Identity.Email == "" {
			// User doesn't have an existing account. Ask them to register.
			u := newLoginURLFromSession(s.IssuerURL, ses, true, []string{ses.ConnectorID}, "register-maybe")
			return u.String(), nil
		}

		// Does the user have an existing account with a different connector?
		if connID, err := getConnectorForUserByEmail(s.UserRepo, ses.Identity.Email); err == nil {
			// Ask user to sign in through existing account.
			u := newLoginURLFromSession(s.IssuerURL, ses, false, []string{connID}, "wrong-connector")
			return u.String(), nil
		}

		// RegisterOnFirstLogin doesn't work for the local connector
		tryToRegister := s.RegisterOnFirstLogin && (ses.ConnectorID != s.localConnectorID)

		if !tryToRegister {
			// User doesn't have an existing account. Ask them to register.
			u := newLoginURLFromSession(s.IssuerURL, ses, true, []string{ses.ConnectorID}, "register-maybe")
			return u.String(), nil
		}

		// First time logging in through a remote connector. Attempt to register.
		emailVerified := conn.TrustedEmailProvider()
		usrID, err := s.UserManager.RegisterWithRemoteIdentity(ses.Identity.Email, emailVerified, remoteIdentity)
		if err != nil {
			return "", fmt.Errorf("failed to register user: %v", err)
		}
		usr, err = s.UserManager.Get(usrID)
		if err != nil {
			return "", fmt.Errorf("getting created user: %v", err)
		}
	} else if err != nil {
		return "", fmt.Errorf("getting user: %v", err)
	}

	if usr.Disabled {
		log.Errorf("user %s disabled", ses.Identity.Email)
		return "", user.ErrorNotFound
	}

	ses, err = s.SessionManager.AttachUser(sessionID, usr.ID)
	if err != nil {
		return "", fmt.Errorf("attaching user to session: %v", err)
	}
	log.Infof("Session %s user identified: clientID=%s user=%#v", sessionID, ses.ClientID, usr)

	code, err := s.SessionManager.NewSessionKey(sessionID)
	if err != nil {
		return "", fmt.Errorf("creating new session key: %v", err)
	}

	ru := ses.RedirectURL
	if ru.String() == client.OOBRedirectURI {
		ru = s.absURL(httpPathOOB)
	}
	q := ru.Query()
	q.Set("code", code)
	q.Set("state", ses.ClientState)
	ru.RawQuery = q.Encode()

	return ru.String(), nil
}