diff --git a/actions_resource.go b/actions_resource.go index 12bee82e8392a3038d73f6c5f75b1bc806d1baaf..012a72865c452dcdd47a47734cbbf17afde6c1d5 100644 --- a/actions_resource.go +++ b/actions_resource.go @@ -1,7 +1,6 @@ package accountserver import ( - "errors" "fmt" "log" ) @@ -196,7 +195,7 @@ func (r *CheckResourceAvailabilityRequest) Validate(rctx *RequestContext) error func (r *CheckResourceAvailabilityRequest) Serve(rctx *RequestContext) (interface{}, error) { var check ValidatorFunc switch r.Type { - case ResourceTypeEmail, ResourceTypeMailingList: + case ResourceTypeEmail, ResourceTypeMailingList, ResourceTypeNewsletter: check = rctx.validationCtx.isAvailableEmailAddr() case ResourceTypeDomain: check = rctx.validationCtx.isAvailableDomain() @@ -207,7 +206,7 @@ func (r *CheckResourceAvailabilityRequest) Serve(rctx *RequestContext) (interfac case ResourceTypeDatabase: check = rctx.validationCtx.isAvailableDatabase() default: - return nil, errors.New("unknown resource type") + return nil, newValidationError(nil, "type", "unknown resource type") } var resp CheckResourceAvailabilityResponse diff --git a/backend/ldap/resources.go b/backend/ldap/resources.go index b20ea52533b7a5c251627efce0ff6a02148784b0..be97114ba4be1d32836ce030199b6f1d8de22ea0 100644 --- a/backend/ldap/resources.go +++ b/backend/ldap/resources.go @@ -100,7 +100,8 @@ func setCommonResourceAttrs(entry *ldap.Entry, rsrc *as.Resource) { func (reg *resourceRegistry) FromLDAP(entry *ldap.Entry) (rsrc *as.Resource, err error) { // Since we don't know what resource type to expect, we try // all known handlers until one returns a valid Resource. - // This is slightly dangerous unless all + // This expects that all object types can be told apart by + // their DN or attributes. for _, h := range reg.handlers { rsrc, err = h.FromLDAP(entry) if err == nil { diff --git a/integrationtest/availability_test.go b/integrationtest/availability_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0ec6f29873355c8d2490ce543c16ef533b605d4e --- /dev/null +++ b/integrationtest/availability_test.go @@ -0,0 +1,52 @@ +package integrationtest + +import ( + "testing" + + as "git.autistici.org/ai3/accountserver" +) + +func TestIntegration_CheckResourceAvailability(t *testing.T) { + stop, _, c := startService(t) + defer stop() + + testdata := []struct { + rtype string + name string + expectedAvail bool + }{ + {"email", "uno@investici.org", false}, + {"email", "due@investici.org", false}, + {"email", "tre@investici.org", false}, + {"email", "newsletter1@investici.org", false}, + {"email", "list4@investici.org", false}, + {"email", "new@investici.org", true}, + + {"list", "newsletter1@investici.org", false}, + {"list", "list4@investici.org", false}, + + {"newsletter", "newsletter1@investici.org", false}, + {"newsletter", "list4@investici.org", false}, + + {"web", "due", false}, + {"web", "new", true}, + + {"dav", "uno", false}, + {"dav", "new", true}, + } + + for _, td := range testdata { + var resp as.CheckResourceAvailabilityResponse + err := c.request("/api/resource/check_availability", &as.CheckResourceAvailabilityRequest{ + Type: td.rtype, + Name: td.name, + }, &resp) + if err != nil { + t.Errorf("CheckResourceAvailability(%s/%s) error: %v", td.rtype, td.name, err) + continue + } + if resp.Available != td.expectedAvail { + t.Errorf("CheckResourceAvailability(%s/%s) returned %v, expected %v", td.rtype, td.name, resp.Available, td.expectedAvail) + } + } +} diff --git a/integrationtest/testdata/test4.ldif b/integrationtest/testdata/test4.ldif index a5c72451a1f67d32fd5982d870a5815674d882a3..283e2b63712ac60f6c854fe3a77d336d08ec6125 100644 --- a/integrationtest/testdata/test4.ldif +++ b/integrationtest/testdata/test4.ldif @@ -41,3 +41,14 @@ status: active uidNumber: 23801 userPassword:: JGEyJDQkMzI3NjgkMSQwZDgyMzU1YjQ0Mzg0M2NmZDY4MjU1MzE4ZTVjYTdiZSRmNTQ0ODkxOTFiNWZlYzk2MDRlNWQ2ODZjMDQxZjJkNTFmOTgxOGY4ZTFmM2E4MDYzY2U3ZTEwMTE3OTc2OGI0 +dn: listName=list4@investici.org,ou=Lists,dc=example,dc=com +objectClass: mailingList +objectClass: top +listName: list4@investici.org +listDescription: Mailing list 4 +public: no +status: active +listOwner: quattro@investici.org +host: host2 +originalHost: host2 + diff --git a/server/server.go b/server/server.go index c4890f05a3e2d908e82599bb51ae4974469d66b7..214753d3378d8cfd6b8bcd4e3fdf8ca5421171cb 100644 --- a/server/server.go +++ b/server/server.go @@ -251,6 +251,8 @@ func errToStatus(err error) int { return http.StatusForbidden case as.IsRequestError(err): return http.StatusBadRequest + case as.IsValidationError(err): + return http.StatusBadRequest default: return http.StatusInternalServerError } diff --git a/validators.go b/validators.go index adff902f0d76ba4835103c60c297f8d0400cc59a..cd169d6b1df0c30b5035f532d51766ceebcb199a 100644 --- a/validators.go +++ b/validators.go @@ -349,14 +349,18 @@ func splitEmailAddr(addr string) (string, string) { // Returns all the possible resources in the email and mailing list // namespaces that might conflict with the given email address. func relatedEmails(ctx context.Context, be domainBackend, addr string) []FindResourceRequest { + // Check for the literal addr in the unified mail+list namespace. rel := []FindResourceRequest{ {Type: ResourceTypeEmail, Name: addr}, + {Type: ResourceTypeMailingList, Name: addr}, + {Type: ResourceTypeNewsletter, Name: addr}, } - user, _ := splitEmailAddr(addr) + // Mailing lists and newsletters must have unique names // regardless of the domain, so we add potential conflicts for // mailing lists with the same name over all list-enabled // domains. + user, _ := splitEmailAddr(addr) for _, d := range be.GetAllowedDomains(ctx, ResourceTypeMailingList) { rel = append(rel, FindResourceRequest{ Type: ResourceTypeMailingList,