240 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			240 lines
		
	
	
		
			5.7 KiB
		
	
	
	
		
			Go
		
	
	
	
package application
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"crypto/rand"
 | 
						|
	"encoding/base64"
 | 
						|
	"errors"
 | 
						|
	"io"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"git.coopgo.io/coopgo-platform/groups-management/grpcapi"
 | 
						|
	mobilityaccounts "git.coopgo.io/coopgo-platform/mobility-accounts/grpcapi"
 | 
						|
	ma "git.coopgo.io/coopgo-platform/mobility-accounts/storage"
 | 
						|
	groupsstorage "git.coopgo.io/coopgo-platform/groups-management/storage"
 | 
						|
	"github.com/coreos/go-oidc/v3/oidc"
 | 
						|
	"github.com/rs/zerolog/log"
 | 
						|
)
 | 
						|
 | 
						|
type OAuth2CallbackResult struct {
 | 
						|
	RedirectURL string
 | 
						|
	IDToken     string
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) ProcessOAuth2Callback(code string, redirectSession string) (*OAuth2CallbackResult, error) {
 | 
						|
	oauth2Token, err := h.idp.OAuth2Config.Exchange(context.Background(), code)
 | 
						|
	if err != nil {
 | 
						|
		log.Error().Err(err).Msg("Exchange error")
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	// Extract the ID Token from OAuth2 token.
 | 
						|
	rawIDToken, ok := oauth2Token.Extra("id_token").(string)
 | 
						|
	if !ok {
 | 
						|
		log.Error().Msg("Cannot retrieve ID token")
 | 
						|
		return nil, errors.New("cannot retrieve ID token")
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = h.idp.TokenVerifier.Verify(context.Background(), rawIDToken)
 | 
						|
	if err != nil {
 | 
						|
		log.Error().Err(err).Msg("Not able to verify token")
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	redirect := "/app/"
 | 
						|
	if redirectSession != "" {
 | 
						|
		redirect = redirectSession
 | 
						|
	}
 | 
						|
 | 
						|
	return &OAuth2CallbackResult{
 | 
						|
		RedirectURL: redirect,
 | 
						|
		IDToken:     rawIDToken,
 | 
						|
	}, nil
 | 
						|
}
 | 
						|
 | 
						|
type LostPasswordInitResult struct {
 | 
						|
	Success bool
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) InitiateLostPassword(email string) (*LostPasswordInitResult, error) {
 | 
						|
	account, err := h.services.GRPC.MobilityAccounts.GetAccountUsername(context.TODO(), &mobilityaccounts.GetAccountUsernameRequest{
 | 
						|
		Username:  email,
 | 
						|
		Namespace: "parcoursmob",
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	b := make([]byte, 16)
 | 
						|
	if _, err := io.ReadFull(rand.Reader, b); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	key := base64.RawURLEncoding.EncodeToString(b)
 | 
						|
 | 
						|
	passwordretrieval := map[string]any{
 | 
						|
		"username":   email,
 | 
						|
		"account_id": account.Account.Id,
 | 
						|
		"key":        key,
 | 
						|
	}
 | 
						|
 | 
						|
	h.cache.PutWithTTL("retrieve-password/"+key, passwordretrieval, 72*time.Hour)
 | 
						|
 | 
						|
	if err := h.emailing.Send("auth.retrieve_password", email, passwordretrieval); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	return &LostPasswordInitResult{Success: true}, nil
 | 
						|
}
 | 
						|
 | 
						|
type LostPasswordRecoverResult struct {
 | 
						|
	Success bool
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) RecoverLostPassword(key, newPassword string) (*LostPasswordRecoverResult, error) {
 | 
						|
	recover, err := h.cache.Get("retrieve-password/" + key)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if newPassword == "" {
 | 
						|
		return nil, errors.New("password is empty")
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = h.services.GRPC.MobilityAccounts.ChangePassword(context.TODO(), &mobilityaccounts.ChangePasswordRequest{
 | 
						|
		Id:       recover.(map[string]any)["account_id"].(string),
 | 
						|
		Password: newPassword,
 | 
						|
	})
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	err = h.cache.Delete("retrieve-password/" + key)
 | 
						|
	if err != nil {
 | 
						|
		log.Error().Err(err).Msg("Failed to delete password recovery key")
 | 
						|
	}
 | 
						|
 | 
						|
	return &LostPasswordRecoverResult{Success: true}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) GetPasswordRecoveryData(key string) (map[string]any, error) {
 | 
						|
	recover, err := h.cache.Get("retrieve-password/" + key)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return recover.(map[string]any), nil
 | 
						|
}
 | 
						|
 | 
						|
type OnboardingResult struct {
 | 
						|
	Success bool
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) CompleteOnboarding(key, password, firstName, lastName string) (*OnboardingResult, error) {
 | 
						|
	onboarding, err := h.cache.Get("onboarding/" + key)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	onboardingmap := onboarding.(map[string]any)
 | 
						|
 | 
						|
	if password == "" {
 | 
						|
		return nil, errors.New("password is empty")
 | 
						|
	}
 | 
						|
 | 
						|
	groups := []string{
 | 
						|
		onboardingmap["group"].(string),
 | 
						|
	}
 | 
						|
 | 
						|
	if onboardingmap["admin"].(bool) {
 | 
						|
		groups = append(groups, onboardingmap["group"].(string)+":admin")
 | 
						|
	}
 | 
						|
 | 
						|
	display_name := firstName + " " + lastName
 | 
						|
	account := &ma.Account{
 | 
						|
		Authentication: ma.AccountAuth{
 | 
						|
			Local: ma.LocalAuth{
 | 
						|
				Username: onboardingmap["username"].(string),
 | 
						|
				Password: password,
 | 
						|
			},
 | 
						|
		},
 | 
						|
		Namespace: "parcoursmob",
 | 
						|
		Data: map[string]any{
 | 
						|
			"display_name": display_name,
 | 
						|
			"first_name":   firstName,
 | 
						|
			"last_name":    lastName,
 | 
						|
			"email":        onboardingmap["username"],
 | 
						|
			"groups":       groups,
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	acc, err := mobilityaccounts.AccountFromStorageType(account)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	request := &mobilityaccounts.RegisterRequest{
 | 
						|
		Account: acc,
 | 
						|
	}
 | 
						|
 | 
						|
	_, err = h.services.GRPC.MobilityAccounts.Register(context.TODO(), request)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	err = h.cache.Delete("onboarding/" + key)
 | 
						|
	if err != nil {
 | 
						|
		log.Error().Err(err).Msg("Failed to delete onboarding key")
 | 
						|
	}
 | 
						|
 | 
						|
	return &OnboardingResult{Success: true}, nil
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) GetOnboardingData(key string) (map[string]any, error) {
 | 
						|
	onboarding, err := h.cache.Get("onboarding/" + key)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	return onboarding.(map[string]any), nil
 | 
						|
}
 | 
						|
 | 
						|
type UserGroupsResult struct {
 | 
						|
	Groups []groupsstorage.Group
 | 
						|
}
 | 
						|
 | 
						|
func (h *ApplicationHandler) GetUserGroups(idtoken *oidc.IDToken) (*UserGroupsResult, error) {
 | 
						|
	var claims map[string]any
 | 
						|
	err := idtoken.Claims(&claims)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	g := claims["groups"]
 | 
						|
	groups_interface, ok := g.([]any)
 | 
						|
	if !ok {
 | 
						|
		return nil, errors.New("invalid groups format")
 | 
						|
	}
 | 
						|
 | 
						|
	groups := []string{}
 | 
						|
	for _, v := range groups_interface {
 | 
						|
		groups = append(groups, v.(string))
 | 
						|
	}
 | 
						|
 | 
						|
	request := &grpcapi.GetGroupsBatchRequest{
 | 
						|
		Groupids: groups,
 | 
						|
	}
 | 
						|
 | 
						|
	resp, err := h.services.GRPC.GroupsManagement.GetGroupsBatch(context.TODO(), request)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	var groupsresponse []groupsstorage.Group
 | 
						|
	for _, group := range resp.Groups {
 | 
						|
		if group.Namespace != "parcoursmob_organizations" {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
		g := group.ToStorageType()
 | 
						|
		groupsresponse = append(groupsresponse, g)
 | 
						|
	}
 | 
						|
 | 
						|
	return &UserGroupsResult{Groups: groupsresponse}, nil
 | 
						|
} |