mirror of
https://github.com/abhinavxd/libredesk.git
synced 2025-11-02 21:13:47 +00:00
Before this feature the only way to create a conversation was by adding inbox and sending an email. Agents first search contacts by email, see a dropdown select an existing contact or fill a new email for new contact. The backend creates contact if it does not exist, creates a conversation, sends a reply to the conversation. Optinally assigns conversation to a user / team. fix: Replies to emails create a new conversation instead of attaching to the previous one. Was not happening in gmail, as gmail was sending the references headers in all replies and I missed this completely. So when libredesk searches a conversation by references headers it worked! Instead the right way is to generate the outgoing email message id and saving it in DB. This commit fixes that. There could be more backup strategies like putting reference number in the subject but that can be explored later. chore: new role `conversatons:write` that enables the create conversations feature for an agent. chore: migrations for v0.4.0.
94 lines
3.4 KiB
Go
94 lines
3.4 KiB
Go
package main
|
|
|
|
import (
|
|
"strconv"
|
|
|
|
amodels "github.com/abhinavxd/libredesk/internal/auth/models"
|
|
"github.com/abhinavxd/libredesk/internal/envelope"
|
|
"github.com/abhinavxd/libredesk/internal/stringutil"
|
|
"github.com/valyala/fasthttp"
|
|
"github.com/zerodha/fastglue"
|
|
)
|
|
|
|
var (
|
|
oidcStateSessKey = "oidc_state"
|
|
)
|
|
|
|
// handleOIDCLogin redirects to the OIDC provider for login.
|
|
func handleOIDCLogin(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
providerID, err = strconv.Atoi(r.RequestCtx.UserValue("id").(string))
|
|
)
|
|
if err != nil {
|
|
app.lo.Error("error parsing provider id", "error", err)
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error parsing provider id.", nil, envelope.GeneralError)
|
|
}
|
|
|
|
// Set a state and save it in the session, to prevent CSRF attacks.
|
|
state, err := stringutil.RandomAlphanumeric(32)
|
|
if err != nil {
|
|
app.lo.Error("error generating state", "error", err)
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error generating state.", nil, envelope.GeneralError)
|
|
}
|
|
if err = app.auth.SetSessionValues(r, map[string]interface{}{
|
|
oidcStateSessKey: state,
|
|
}); err != nil {
|
|
app.lo.Error("error saving state in session", "error", err)
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error saving state in session.", nil, envelope.GeneralError)
|
|
}
|
|
|
|
authURL, err := app.auth.LoginURL(providerID, state)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
return r.Redirect(authURL, fasthttp.StatusFound, nil, "")
|
|
}
|
|
|
|
// handleOIDCCallback receives the redirect callback from the OIDC provider and completes the handshake.
|
|
func handleOIDCCallback(r *fastglue.Request) error {
|
|
var (
|
|
app = r.Context.(*App)
|
|
code = string(r.RequestCtx.QueryArgs().Peek("code"))
|
|
state = string(r.RequestCtx.QueryArgs().Peek("state"))
|
|
providerID, err = strconv.Atoi(string(r.RequestCtx.UserValue("id").(string)))
|
|
)
|
|
if err != nil {
|
|
app.lo.Error("error parsing provider id", "error", err)
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error parsing provider id.", nil, envelope.GeneralError)
|
|
}
|
|
|
|
// Compare the state from the session with the state from the query.
|
|
sessionState, err := app.auth.GetSessionValue(r, oidcStateSessKey)
|
|
if err != nil {
|
|
app.lo.Error("error getting state from session", "error", err)
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error getting state from session.", nil, envelope.GeneralError)
|
|
}
|
|
if state != sessionState {
|
|
return r.SendErrorEnvelope(fasthttp.StatusForbidden, "Invalid state.", nil, envelope.GeneralError)
|
|
}
|
|
|
|
_, claims, err := app.auth.ExchangeOIDCToken(r.RequestCtx, providerID, code)
|
|
if err != nil {
|
|
app.lo.Error("error exchanging oidc token", "error", err)
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error exchanging OIDC token.", nil, envelope.GeneralError)
|
|
}
|
|
|
|
// Lookup the user by email and set the session.
|
|
user, err := app.user.GetAgentByEmail(claims.Email)
|
|
if err != nil {
|
|
return sendErrorEnvelope(r, err)
|
|
}
|
|
|
|
if err := app.auth.SaveSession(amodels.User{
|
|
ID: user.ID,
|
|
Email: user.Email.String,
|
|
FirstName: user.FirstName,
|
|
LastName: user.LastName,
|
|
}, r); err != nil {
|
|
return r.SendErrorEnvelope(fasthttp.StatusInternalServerError, "Error saving session.", nil, envelope.GeneralError)
|
|
}
|
|
|
|
return r.Redirect("/", fasthttp.StatusFound, nil, "")
|
|
}
|