diff --git a/web/sharings/sharings.go b/web/sharings/sharings.go index ad6f815fd71..1747d41496a 100644 --- a/web/sharings/sharings.go +++ b/web/sharings/sharings.go @@ -686,6 +686,20 @@ func renderAlreadyAccepted(c echo.Context, inst *instance.Instance, cozyURL stri }) } +func renderInvalidSharing(c echo.Context, inst *instance.Instance) error { + return c.Render(http.StatusBadRequest, "error.html", echo.Map{ + "Domain": inst.ContextualDomain(), + "ContextName": inst.ContextName, + "Locale": inst.Locale, + "Title": inst.TemplateTitle(), + "Favicon": middlewares.Favicon(inst), + "Illustration": "/images/generic-error.svg", + "Error": "Error Invalid sharing", + "SupportEmail": inst.SupportEmailAddress(), + "SupportPageURL": inst.SupportPageURL(), + }) +} + func renderDiscoveryForm(c echo.Context, inst *instance.Instance, code int, sharingID, state, sharecode, shortcut string, m *sharing.Member) error { publicName, _ := settings.PublicName(inst) fqdn := strings.TrimPrefix(m.Instance, "https://") @@ -774,6 +788,20 @@ func renderDiscoveryForm(c echo.Context, inst *instance.Instance, code int, shar }) } +func discoveryStateForMember(s *sharing.Sharing, currentState string, m *sharing.Member) (string, error) { + if currentState != "" { + return currentState, nil + } + if m == nil { + return "", sharing.ErrInvalidSharing + } + credentials := s.FindCredentials(m) + if credentials == nil || credentials.State == "" { + return "", sharing.ErrInvalidSharing + } + return credentials.State, nil +} + // GetDiscovery displays a form where a recipient can give the address of their // cozy instance func GetDiscovery(c echo.Context) error { @@ -785,17 +813,7 @@ func GetDiscovery(c echo.Context) error { s, err := sharing.FindSharing(inst, sharingID) if err != nil { - return c.Render(http.StatusBadRequest, "error.html", echo.Map{ - "Domain": inst.ContextualDomain(), - "ContextName": inst.ContextName, - "Locale": inst.Locale, - "Title": inst.TemplateTitle(), - "Favicon": middlewares.Favicon(inst), - "Illustration": "/images/generic-error.svg", - "Error": "Error Invalid sharing", - "SupportEmail": inst.SupportEmailAddress(), - "SupportPageURL": inst.SupportPageURL(), - }) + return renderInvalidSharing(c, inst) } m := &sharing.Member{} @@ -806,23 +824,16 @@ func GetDiscovery(c echo.Context) error { m, err = s.FindMemberByState(state) } if err != nil || m.Status == sharing.MemberStatusRevoked { - return c.Render(http.StatusBadRequest, "error.html", echo.Map{ - "Domain": inst.ContextualDomain(), - "ContextName": inst.ContextName, - "Locale": inst.Locale, - "Title": inst.TemplateTitle(), - "Favicon": middlewares.Favicon(inst), - "Illustration": "/images/generic-error.svg", - "Error": "Error Invalid sharing", - "SupportEmail": inst.SupportEmailAddress(), - "SupportPageURL": inst.SupportPageURL(), - }) + return renderInvalidSharing(c, inst) } if m.Status != sharing.MemberStatusMailNotSent && m.Status != sharing.MemberStatusPendingInvitation && m.Status != sharing.MemberStatusSeen { return renderAlreadyAccepted(c, inst, m.Instance) } + if state, err = discoveryStateForMember(s, state, m); err != nil { + return renderInvalidSharing(c, inst) + } } if m.Instance != "" { @@ -886,6 +897,9 @@ func PostDiscovery(c echo.Context) error { return wrapErrors(err) } } + if state, err = discoveryStateForMember(s, state, member); err != nil { + return wrapErrors(err) + } if strings.Contains(cozyURL, "@") { return renderDiscoveryForm(c, inst, http.StatusPreconditionFailed, sharingID, state, sharecode, shortcut, member) } diff --git a/web/sharings/sharings_test.go b/web/sharings/sharings_test.go index f9b1ef8a4a1..12bf72772f2 100644 --- a/web/sharings/sharings_test.go +++ b/web/sharings/sharings_test.go @@ -428,6 +428,57 @@ func TestSharings(t *testing.T) { assert.Contains(t, discoveryLink, "/preview?sharecode=") }) + t.Run("DiscoveryWithPreviewOIDCLinkUsesMemberState", func(t *testing.T) { + u, err := url.Parse(discoveryLink) + require.NoError(t, err) + sharecode := u.Query().Get("sharecode") + require.NotEmpty(t, sharecode) + + s, err := sharing.FindSharing(aliceInstance, sharingID) + require.NoError(t, err) + member, err := s.FindMemberBySharecode(aliceInstance, sharecode) + require.NoError(t, err) + credentials := s.FindCredentials(member) + require.NotNil(t, credentials) + require.NotEmpty(t, credentials.State) + + conf := config.GetConfig() + if conf.Authentication == nil { + conf.Authentication = make(map[string]interface{}) + } + oidcContext := "preview-oidc-test" + originalContext := aliceInstance.ContextName + previousAuth, hadPreviousAuth := conf.Authentication[oidcContext] + conf.Authentication[oidcContext] = map[string]interface{}{ + "oidc": map[string]interface{}{ + "client_id": "preview-oidc-client", + }, + } + aliceInstance.ContextName = oidcContext + require.NoError(t, instance.Update(aliceInstance)) + t.Cleanup(func() { + if hadPreviousAuth { + conf.Authentication[oidcContext] = previousAuth + } else { + delete(conf.Authentication, oidcContext) + } + aliceInstance.ContextName = originalContext + if err := instance.Update(aliceInstance); err != nil { + t.Errorf("cannot restore Alice context: %s", err) + } + }) + + eA := httpexpect.Default(t, tsA.URL) + body := eA.GET("/sharings/"+sharingID+"/discovery"). + WithQuery("sharecode", sharecode). + Expect().Status(200). + Body().Raw() + + assert.Contains(t, body, "/oidc/sharing?") + assert.Contains(t, body, "state="+credentials.State) + assert.NotContains(t, body, "state=&") + }) + t.Run("DiscoveryWithPreview", func(t *testing.T) { u, err := url.Parse(discoveryLink) assert.NoError(t, err)