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