diff --git a/backend/composite_values.go b/backend/composite_values.go index c6c57006c0bd5714e8a7f1da6f09c9c331fbe7f5..2d5f84b00028a985b2f02a5ca22ad89bc461602f 100644 --- a/backend/composite_values.go +++ b/backend/composite_values.go @@ -5,13 +5,13 @@ import ( "fmt" "strings" - "git.autistici.org/ai3/accountserver" + as "git.autistici.org/ai3/accountserver" ) // Extend the AppSpecificPasswordInfo type, which only contains public // information, with the encrypted password. type appSpecificPassword struct { - accountserver.AppSpecificPasswordInfo + as.AppSpecificPasswordInfo Password string } @@ -23,7 +23,7 @@ func (p *appSpecificPassword) Encode() string { }, ":") } -func newAppSpecificPassword(info accountserver.AppSpecificPasswordInfo, pw string) *appSpecificPassword { +func newAppSpecificPassword(info as.AppSpecificPasswordInfo, pw string) *appSpecificPassword { return &appSpecificPassword{ AppSpecificPasswordInfo: info, Password: pw, @@ -35,7 +35,7 @@ func parseAppSpecificPassword(asp string) (*appSpecificPassword, error) { if len(parts) != 3 { return nil, errors.New("badly encoded app-specific password") } - return newAppSpecificPassword(accountserver.AppSpecificPasswordInfo{ + return newAppSpecificPassword(as.AppSpecificPasswordInfo{ Service: parts[0], Comment: parts[2], }, parts[1]), nil @@ -59,22 +59,22 @@ func encodeAppSpecificPasswords(asps []*appSpecificPassword) []string { return out } -func getASPInfo(asps []*appSpecificPassword) []*accountserver.AppSpecificPasswordInfo { - var out []*accountserver.AppSpecificPasswordInfo +func getASPInfo(asps []*appSpecificPassword) []*as.AppSpecificPasswordInfo { + var out []*as.AppSpecificPasswordInfo for _, asp := range asps { out = append(out, &asp.AppSpecificPasswordInfo) } return out } -func decodeUserEncryptionKeys(values []string) []*accountserver.UserEncryptionKey { - var out []*accountserver.UserEncryptionKey +func decodeUserEncryptionKeys(values []string) []*as.UserEncryptionKey { + var out []*as.UserEncryptionKey for _, value := range values { idx := strings.IndexByte(value, ':') if idx < 0 { continue } - out = append(out, &accountserver.UserEncryptionKey{ + out = append(out, &as.UserEncryptionKey{ ID: value[:idx], Key: []byte(value[idx+1:]), }) @@ -82,7 +82,7 @@ func decodeUserEncryptionKeys(values []string) []*accountserver.UserEncryptionKe return out } -func encodeUserEncryptionKeys(keys []*accountserver.UserEncryptionKey) []string { +func encodeUserEncryptionKeys(keys []*as.UserEncryptionKey) []string { var out []string for _, key := range keys { out = append(out, fmt.Sprintf("%s:%s", key.ID, string(key.Key))) diff --git a/backend/model.go b/backend/model.go index 31a07bce8765709f36163bd3ef1d5e4f790b3cae..8946b4e5a2c048bcfd02755e01bbf3f1a00d320a 100644 --- a/backend/model.go +++ b/backend/model.go @@ -11,7 +11,7 @@ import ( "github.com/tstranex/u2f" "gopkg.in/ldap.v2" - "git.autistici.org/ai3/accountserver" + as "git.autistici.org/ai3/accountserver" ) const ( @@ -59,7 +59,7 @@ type backendTX struct { const ldapPoolSize = 20 -func (b *backend) NewTransaction() (accountserver.TX, error) { +func (b *backend) NewTransaction() (as.TX, error) { return &backendTX{ ldapTX: newLDAPTX(b.conn), backend: b, @@ -68,7 +68,7 @@ func (b *backend) NewTransaction() (accountserver.TX, error) { // NewLDAPBackend initializes an LDAPBackend object with the given LDAP // connection pool. -func NewLDAPBackend(uri, bindDN, bindPw, base string) (accountserver.Backend, error) { +func NewLDAPBackend(uri, bindDN, bindPw, base string) (as.Backend, error) { pool, err := ldaputil.NewConnectionPool(uri, bindDN, bindPw, ldapPoolSize) if err != nil { return nil, err @@ -78,12 +78,12 @@ func NewLDAPBackend(uri, bindDN, bindPw, base string) (accountserver.Backend, er func newLDAPBackendWithConn(conn ldapConn, base string) (*backend, error) { rsrc := newResourceRegistry() - rsrc.register(accountserver.ResourceTypeEmail, &emailResourceHandler{baseDN: base}) - rsrc.register(accountserver.ResourceTypeMailingList, &mailingListResourceHandler{baseDN: base}) - rsrc.register(accountserver.ResourceTypeDAV, &webdavResourceHandler{baseDN: base}) - rsrc.register(accountserver.ResourceTypeWebsite, &websiteResourceHandler{baseDN: base}) - rsrc.register(accountserver.ResourceTypeDomain, &domainResourceHandler{baseDN: base}) - rsrc.register(accountserver.ResourceTypeDatabase, &databaseResourceHandler{baseDN: base}) + rsrc.register(as.ResourceTypeEmail, &emailResourceHandler{baseDN: base}) + rsrc.register(as.ResourceTypeMailingList, &mailingListResourceHandler{baseDN: base}) + rsrc.register(as.ResourceTypeDAV, &webdavResourceHandler{baseDN: base}) + rsrc.register(as.ResourceTypeWebsite, &websiteResourceHandler{baseDN: base}) + rsrc.register(as.ResourceTypeDomain, &domainResourceHandler{baseDN: base}) + rsrc.register(as.ResourceTypeDatabase, &databaseResourceHandler{baseDN: base}) return &backend{ conn: conn, @@ -112,7 +112,7 @@ func newLDAPBackendWithConn(conn ldapConn, base string) (*backend, error) { }, nil } -func newUser(entry *ldap.Entry) (*accountserver.User, error) { +func newUser(entry *ldap.Entry) (*as.User, error) { // Note that some user-level attributes related to // authentication are stored on the uid= object, while others // are on the email= object. We set the latter in the GetUser @@ -121,8 +121,8 @@ func newUser(entry *ldap.Entry) (*accountserver.User, error) { // The case of password recovery attributes is more complex: // the current schema has those on email=, but we'd like to // move them to uid=, so we currently have to support both. - uidNumber, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) - user := &accountserver.User{ + uidNumber, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) // nolint + user := &as.User{ Name: entry.GetAttributeValue("uid"), Lang: entry.GetAttributeValue(preferredLanguageLDAPAttr), UID: uidNumber, @@ -140,7 +140,7 @@ func newUser(entry *ldap.Entry) (*accountserver.User, error) { return user, nil } -func userToLDAP(user *accountserver.User) (attrs []ldap.PartialAttribute) { +func userToLDAP(user *as.User) (attrs []ldap.PartialAttribute) { // Most attributes are read-only and have specialized methods to set them. attrs = append(attrs, []ldap.PartialAttribute{ {Type: "objectClass", Vals: []string{"top", "person", "posixAccount", "shadowAccount", "organizationalPerson", "inetOrgPerson", "totpAccount"}}, @@ -161,21 +161,22 @@ func userToLDAP(user *accountserver.User) (attrs []ldap.PartialAttribute) { return } -func decodeU2FRegistration(enc string) (*u2f.Registration, error) { +func decodeU2FRegistration(enc string) (*as.U2FRegistration, error) { var reg u2f.Registration if err := reg.UnmarshalBinary([]byte(enc)); err != nil { return nil, err } - return ®, nil + return &as.U2FRegistration{Registration: ®}, nil } -func encodeU2FRegistration(r *u2f.Registration) string { - b, _ := r.MarshalBinary() +func encodeU2FRegistration(r *as.U2FRegistration) string { + // MarshalBinary can't fail, ignore error. + b, _ := r.MarshalBinary() // nolint return string(b) } -func decodeU2FRegistrations(encRegs []string) []*u2f.Registration { - var out []*u2f.Registration +func decodeU2FRegistrations(encRegs []string) []*as.U2FRegistration { + var out []*as.U2FRegistration for _, enc := range encRegs { if r, err := decodeU2FRegistration(enc); err == nil { out = append(out, r) @@ -184,7 +185,7 @@ func decodeU2FRegistrations(encRegs []string) []*u2f.Registration { return out } -func encodeU2FRegistrations(regs []*u2f.Registration) []string { +func encodeU2FRegistrations(regs []*as.U2FRegistration) []string { var out []string for _, r := range regs { out = append(out, encodeU2FRegistration(r)) @@ -192,12 +193,12 @@ func encodeU2FRegistrations(regs []*u2f.Registration) []string { return out } -func (tx *backendTX) getUserDN(user *accountserver.User) string { +func (tx *backendTX) getUserDN(user *as.User) string { return joinDN("uid="+user.Name, "ou=People", tx.backend.baseDN) } // CreateUser creates a new user. -func (tx *backendTX) CreateUser(ctx context.Context, user *accountserver.User) error { +func (tx *backendTX) CreateUser(ctx context.Context, user *as.User) error { dn := tx.getUserDN(user) tx.create(dn) @@ -216,7 +217,7 @@ func (tx *backendTX) CreateUser(ctx context.Context, user *accountserver.User) e } // UpdateUser updates values for the user only (not the resources). -func (tx *backendTX) UpdateUser(ctx context.Context, user *accountserver.User) error { +func (tx *backendTX) UpdateUser(ctx context.Context, user *as.User) error { dn := tx.getUserDN(user) for _, attr := range userToLDAP(user) { tx.setAttr(dn, attr.Type, attr.Vals...) @@ -225,7 +226,7 @@ func (tx *backendTX) UpdateUser(ctx context.Context, user *accountserver.User) e } // GetUser returns a user. -func (tx *backendTX) GetUser(ctx context.Context, username string) (*accountserver.User, error) { +func (tx *backendTX) GetUser(ctx context.Context, username string) (*as.User, error) { // First of all, find the main user object, and just that one. vars := map[string]string{"user": username} result, err := tx.search(ctx, tx.backend.userQuery.query(vars)) @@ -276,17 +277,20 @@ func (tx *backendTX) GetUser(ctx context.Context, username string) (*accountserv return user, nil } -func (tx *backendTX) SetUserPassword(ctx context.Context, user *accountserver.User, encryptedPassword string) error { +func (tx *backendTX) SetUserPassword(ctx context.Context, user *as.User, encryptedPassword string) (err error) { dn := tx.getUserDN(user) tx.setAttr(dn, passwordLDAPAttr, encryptedPassword) - for _, r := range user.GetResourcesByType(accountserver.ResourceTypeEmail) { - dn, _ = tx.backend.resources.GetDN(r.ID) + for _, r := range user.GetResourcesByType(as.ResourceTypeEmail) { + dn, err = tx.backend.resources.GetDN(r.ID) + if err != nil { + return + } tx.setAttr(dn, passwordLDAPAttr, encryptedPassword) } - return nil + return } -func (tx *backendTX) GetUserEncryptedPassword(ctx context.Context, user *accountserver.User) string { +func (tx *backendTX) GetUserEncryptedPassword(ctx context.Context, user *as.User) string { values := tx.readAttributeValues(ctx, tx.getUserDN(user), passwordLDAPAttr) if len(values) > 0 { // Remove legacy LDAP encryption prefix. @@ -295,7 +299,7 @@ func (tx *backendTX) GetUserEncryptedPassword(ctx context.Context, user *account return "" } -func (tx *backendTX) GetUserRecoveryEncryptedPassword(ctx context.Context, user *accountserver.User) string { +func (tx *backendTX) GetUserRecoveryEncryptedPassword(ctx context.Context, user *as.User) string { values := tx.readAttributeValues(ctx, tx.getUserDN(user), recoveryResponseLDAPAttr) if len(values) > 0 { // Remove legacy LDAP encryption prefix. @@ -304,7 +308,7 @@ func (tx *backendTX) GetUserRecoveryEncryptedPassword(ctx context.Context, user return "" } -func (tx *backendTX) SetPasswordRecoveryHint(ctx context.Context, user *accountserver.User, hint, response string) error { +func (tx *backendTX) SetPasswordRecoveryHint(ctx context.Context, user *as.User, hint, response string) error { // Write the password recovery attributes on the uid= object, // as per the new schema. dn := tx.getUserDN(user) @@ -313,25 +317,34 @@ func (tx *backendTX) SetPasswordRecoveryHint(ctx context.Context, user *accounts return nil } -func (tx *backendTX) GetUserEncryptionKeys(ctx context.Context, user *accountserver.User) ([]*accountserver.UserEncryptionKey, error) { - r := user.GetSingleResourceByType(accountserver.ResourceTypeEmail) - dn, _ := tx.backend.resources.GetDN(r.ID) +func (tx *backendTX) GetUserEncryptionKeys(ctx context.Context, user *as.User) ([]*as.UserEncryptionKey, error) { + r := user.GetSingleResourceByType(as.ResourceTypeEmail) + dn, err := tx.backend.resources.GetDN(r.ID) + if err != nil { + return nil, err + } rawKeys := tx.readAttributeValues(ctx, dn, storagePrivateKeyLDAPAttr) return decodeUserEncryptionKeys(rawKeys), nil } -func (tx *backendTX) SetUserEncryptionKeys(ctx context.Context, user *accountserver.User, keys []*accountserver.UserEncryptionKey) error { +func (tx *backendTX) SetUserEncryptionKeys(ctx context.Context, user *as.User, keys []*as.UserEncryptionKey) error { encKeys := encodeUserEncryptionKeys(keys) - for _, r := range user.GetResourcesByType(accountserver.ResourceTypeEmail) { - dn, _ := tx.backend.resources.GetDN(r.ID) + for _, r := range user.GetResourcesByType(as.ResourceTypeEmail) { + dn, err := tx.backend.resources.GetDN(r.ID) + if err != nil { + return err + } tx.setAttr(dn, storagePrivateKeyLDAPAttr, encKeys...) } return nil } -func (tx *backendTX) SetUserEncryptionPublicKey(ctx context.Context, user *accountserver.User, pub []byte) error { - for _, r := range user.GetResourcesByType(accountserver.ResourceTypeEmail) { - dn, _ := tx.backend.resources.GetDN(r.ID) +func (tx *backendTX) SetUserEncryptionPublicKey(ctx context.Context, user *as.User, pub []byte) error { + for _, r := range user.GetResourcesByType(as.ResourceTypeEmail) { + dn, err := tx.backend.resources.GetDN(r.ID) + if err != nil { + return err + } tx.setAttr(dn, storagePublicKeyLDAPAttr, string(pub)) } return nil @@ -347,8 +360,11 @@ func excludeASPFromList(asps []*appSpecificPassword, id string) []*appSpecificPa return out } -func (tx *backendTX) setASPOnResource(ctx context.Context, r *accountserver.Resource, info *accountserver.AppSpecificPasswordInfo, encryptedPassword string) { - dn, _ := tx.backend.resources.GetDN(r.ID) +func (tx *backendTX) setASPOnResource(ctx context.Context, r *as.Resource, info *as.AppSpecificPasswordInfo, encryptedPassword string) { + dn, err := tx.backend.resources.GetDN(r.ID) + if err != nil { + return + } // Obtain the full list of ASPs from the backend and replace/append the new one. asps := decodeAppSpecificPasswords(tx.readAttributeValues(ctx, dn, aspLDAPAttr)) @@ -357,40 +373,46 @@ func (tx *backendTX) setASPOnResource(ctx context.Context, r *accountserver.Reso tx.setAttr(dn, aspLDAPAttr, outASPs...) } -func (tx *backendTX) SetApplicationSpecificPassword(ctx context.Context, user *accountserver.User, info *accountserver.AppSpecificPasswordInfo, encryptedPassword string) error { - for _, r := range user.GetResourcesByType(accountserver.ResourceTypeEmail) { +func (tx *backendTX) SetApplicationSpecificPassword(ctx context.Context, user *as.User, info *as.AppSpecificPasswordInfo, encryptedPassword string) error { + for _, r := range user.GetResourcesByType(as.ResourceTypeEmail) { tx.setASPOnResource(ctx, r, info, encryptedPassword) } return nil } -func (tx *backendTX) deleteASPOnResource(ctx context.Context, r *accountserver.Resource, id string) { - dn, _ := tx.backend.resources.GetDN(r.ID) +func (tx *backendTX) deleteASPOnResource(ctx context.Context, r *as.Resource, id string) { + dn, err := tx.backend.resources.GetDN(r.ID) + if err != nil { + return + } asps := decodeAppSpecificPasswords(tx.readAttributeValues(ctx, dn, aspLDAPAttr)) asps = excludeASPFromList(asps, id) outASPs := encodeAppSpecificPasswords(asps) tx.setAttr(dn, aspLDAPAttr, outASPs...) } -func (tx *backendTX) DeleteApplicationSpecificPassword(ctx context.Context, user *accountserver.User, id string) error { - for _, r := range user.GetResourcesByType(accountserver.ResourceTypeEmail) { +func (tx *backendTX) DeleteApplicationSpecificPassword(ctx context.Context, user *as.User, id string) error { + for _, r := range user.GetResourcesByType(as.ResourceTypeEmail) { tx.deleteASPOnResource(ctx, r, id) } return nil } -func (tx *backendTX) SetUserTOTPSecret(ctx context.Context, user *accountserver.User, secret string) error { +func (tx *backendTX) SetUserTOTPSecret(ctx context.Context, user *as.User, secret string) error { tx.setAttr(tx.getUserDN(user), totpSecretLDAPAttr, secret) return nil } -func (tx *backendTX) DeleteUserTOTPSecret(ctx context.Context, user *accountserver.User) error { +func (tx *backendTX) DeleteUserTOTPSecret(ctx context.Context, user *as.User) error { tx.setAttr(tx.getUserDN(user), totpSecretLDAPAttr) return nil } -func (tx *backendTX) SetResourcePassword(ctx context.Context, r *accountserver.Resource, encryptedPassword string) error { - dn, _ := tx.backend.resources.GetDN(r.ID) +func (tx *backendTX) SetResourcePassword(ctx context.Context, r *as.Resource, encryptedPassword string) error { + dn, err := tx.backend.resources.GetDN(r.ID) + if err != nil { + return err + } tx.setAttr(dn, passwordLDAPAttr, encryptedPassword) return nil } @@ -420,7 +442,7 @@ func (tx *backendTX) hasResource(ctx context.Context, resourceType, resourceName } // HasAnyResource returns true if any of the specified resources exists. -func (tx *backendTX) HasAnyResource(ctx context.Context, resourceIDs []accountserver.FindResourceRequest) (bool, error) { +func (tx *backendTX) HasAnyResource(ctx context.Context, resourceIDs []as.FindResourceRequest) (bool, error) { for _, req := range resourceIDs { has, err := tx.hasResource(ctx, req.Type, req.Name) if err != nil || has { @@ -431,7 +453,7 @@ func (tx *backendTX) HasAnyResource(ctx context.Context, resourceIDs []accountse } // GetResource returns a ResourceWrapper, as part of a read-modify-update transaction. -func (tx *backendTX) GetResource(ctx context.Context, rsrcID accountserver.ResourceID) (*accountserver.Resource, error) { +func (tx *backendTX) GetResource(ctx context.Context, rsrcID as.ResourceID) (*as.Resource, error) { // From the resource ID we can obtain the DN, and fetch it // straight from LDAP without even doing a real search. dn, err := tx.backend.resources.GetDN(rsrcID) @@ -465,7 +487,7 @@ func (tx *backendTX) GetResource(ctx context.Context, rsrcID accountserver.Resou } // CreateResource creates a new LDAP-backed resource object. -func (tx *backendTX) CreateResource(ctx context.Context, r *accountserver.Resource) error { +func (tx *backendTX) CreateResource(ctx context.Context, r *as.Resource) error { dn, err := tx.backend.resources.GetDN(r.ID) if err != nil { return err @@ -480,7 +502,7 @@ func (tx *backendTX) CreateResource(ctx context.Context, r *accountserver.Resour } // UpdateResource updates a LDAP-backed resource that was obtained by a previous GetResource call. -func (tx *backendTX) UpdateResource(ctx context.Context, r *accountserver.Resource) error { +func (tx *backendTX) UpdateResource(ctx context.Context, r *as.Resource) error { dn, err := tx.backend.resources.GetDN(r.ID) if err != nil { return err diff --git a/backend/model_test.go b/backend/model_test.go index 365f6d2d43bdf4594c4a5c42687201c627fe89fc..05a3c1fa6f1de0d6d7659ca7fe50cb11894236d6 100644 --- a/backend/model_test.go +++ b/backend/model_test.go @@ -6,7 +6,7 @@ import ( "github.com/go-test/deep" - "git.autistici.org/ai3/accountserver" + as "git.autistici.org/ai3/accountserver" "git.autistici.org/ai3/accountserver/ldaptest" ) @@ -17,15 +17,15 @@ const ( testUser2 = "due@investici.org" ) -func startServerAndGetUser(t testing.TB) (func(), accountserver.Backend, *accountserver.User) { +func startServerAndGetUser(t testing.TB) (func(), as.Backend, *as.User) { return startServerAndGetUserWithName(t, testUser1) } -func startServerAndGetUser2(t testing.TB) (func(), accountserver.Backend, *accountserver.User) { +func startServerAndGetUser2(t testing.TB) (func(), as.Backend, *as.User) { return startServerAndGetUserWithName(t, testUser2) } -func startServer(t testing.TB) (func(), accountserver.Backend) { +func startServer(t testing.TB) (func(), as.Backend) { stop := ldaptest.StartServer(t, &ldaptest.Config{ Dir: "../ldaptest", Port: testLDAPPort, @@ -45,7 +45,7 @@ func startServer(t testing.TB) (func(), accountserver.Backend) { return stop, b } -func startServerAndGetUserWithName(t testing.TB, username string) (func(), accountserver.Backend, *accountserver.User) { +func startServerAndGetUserWithName(t testing.TB, username string) (func(), as.Backend, *as.User) { stop, b := startServer(t) tx, _ := b.NewTransaction() @@ -86,25 +86,25 @@ func TestModel_GetUser(t *testing.T) { } // Test a specific resource (the database). - db := user.GetSingleResourceByType(accountserver.ResourceTypeDatabase) - expectedDB := &accountserver.Resource{ - ID: accountserver.NewResourceID( - accountserver.ResourceTypeDatabase, + db := user.GetSingleResourceByType(as.ResourceTypeDatabase) + expectedDB := &as.Resource{ + ID: as.NewResourceID( + as.ResourceTypeDatabase, testUser1, "alias=uno", "unodb", ), - Type: accountserver.ResourceTypeDatabase, - ParentID: accountserver.NewResourceID( - accountserver.ResourceTypeWebsite, + Type: as.ResourceTypeDatabase, + ParentID: as.NewResourceID( + as.ResourceTypeWebsite, testUser1, "uno", ), Name: "unodb", Shard: "host2", OriginalShard: "host2", - Status: accountserver.ResourceStatusActive, - Database: &accountserver.Database{ + Status: as.ResourceStatusActive, + Database: &as.Database{ CleartextPassword: "password", DBUser: "unodb", }, @@ -168,7 +168,7 @@ func TestModel_SetResourceStatus(t *testing.T) { } tx, _ := b.NewTransaction() - rsrcID := accountserver.NewResourceID(accountserver.ResourceTypeEmail, testUser1, testUser1) + rsrcID := as.NewResourceID(as.ResourceTypeEmail, testUser1, testUser1) r, err := tx.GetResource(context.Background(), rsrcID) if err != nil { t.Fatal("GetResource", err) @@ -177,7 +177,7 @@ func TestModel_SetResourceStatus(t *testing.T) { t.Fatalf("could not find test resource %s", rsrcID) } - r.Status = accountserver.ResourceStatusInactive + r.Status = as.ResourceStatusInactive if err := tx.UpdateResource(context.Background(), r); err != nil { t.Fatal("UpdateResource", err) } @@ -203,9 +203,9 @@ func TestModel_HasAnyResource(t *testing.T) { tx, _ := b.NewTransaction() // Request that should succeed. - ok, err := tx.HasAnyResource(context.Background(), []accountserver.FindResourceRequest{ - {Type: accountserver.ResourceTypeEmail, Name: "foo"}, - {Type: accountserver.ResourceTypeEmail, Name: testUser1}, + ok, err := tx.HasAnyResource(context.Background(), []as.FindResourceRequest{ + {Type: as.ResourceTypeEmail, Name: "foo"}, + {Type: as.ResourceTypeEmail, Name: testUser1}, }) if err != nil { t.Fatal("HasAnyResource", err) @@ -215,8 +215,8 @@ func TestModel_HasAnyResource(t *testing.T) { } // Request that should fail (bad resource type). - ok, err = tx.HasAnyResource(context.Background(), []accountserver.FindResourceRequest{ - {Type: accountserver.ResourceTypeDatabase, Name: testUser1}, + ok, err = tx.HasAnyResource(context.Background(), []as.FindResourceRequest{ + {Type: as.ResourceTypeDatabase, Name: testUser1}, }) if err != nil { t.Fatal("HasAnyResource", err) @@ -262,9 +262,9 @@ func TestModel_SetUserEncryptionKeys_Add(t *testing.T) { defer stop() tx, _ := b.NewTransaction() - keys := []*accountserver.UserEncryptionKey{ + keys := []*as.UserEncryptionKey{ { - ID: accountserver.UserEncryptionKeyMainID, + ID: as.UserEncryptionKeyMainID, Key: []byte("very secret key"), }, } @@ -281,9 +281,9 @@ func TestModel_SetUserEncryptionKeys_Replace(t *testing.T) { defer stop() tx, _ := b.NewTransaction() - keys := []*accountserver.UserEncryptionKey{ + keys := []*as.UserEncryptionKey{ { - ID: accountserver.UserEncryptionKeyMainID, + ID: as.UserEncryptionKeyMainID, Key: []byte("very secret key"), }, } diff --git a/backend/resources.go b/backend/resources.go index 5366d2269527cd2f7fc930ff8a744492261e51c5..52423dea0b614cf5eaaa2c77e5c5dd74d5184210 100644 --- a/backend/resources.go +++ b/backend/resources.go @@ -8,15 +8,15 @@ import ( "gopkg.in/ldap.v2" - "git.autistici.org/ai3/accountserver" + as "git.autistici.org/ai3/accountserver" ) // Generic resource handler interface. One for each resource type, // mapping to exactly one LDAP object type. type resourceHandler interface { - GetDN(accountserver.ResourceID) (string, error) - ToLDAP(*accountserver.Resource) []ldap.PartialAttribute - FromLDAP(*ldap.Entry) (*accountserver.Resource, error) + GetDN(as.ResourceID) (string, error) + ToLDAP(*as.Resource) []ldap.PartialAttribute + FromLDAP(*ldap.Entry) (*as.Resource, error) SearchQuery() *queryTemplate } @@ -47,7 +47,7 @@ func (reg *resourceRegistry) dispatch(rsrcType string, f func(resourceHandler) e return f(h) } -func (reg *resourceRegistry) GetDN(id accountserver.ResourceID) (s string, err error) { +func (reg *resourceRegistry) GetDN(id as.ResourceID) (s string, err error) { err = reg.dispatch(id.Type(), func(h resourceHandler) (herr error) { s, herr = h.GetDN(id) return @@ -55,7 +55,7 @@ func (reg *resourceRegistry) GetDN(id accountserver.ResourceID) (s string, err e return } -func (reg *resourceRegistry) ToLDAP(rsrc *accountserver.Resource) (attrs []ldap.PartialAttribute) { +func (reg *resourceRegistry) ToLDAP(rsrc *as.Resource) (attrs []ldap.PartialAttribute) { if err := reg.dispatch(rsrc.ID.Type(), func(h resourceHandler) error { attrs = h.ToLDAP(rsrc) return nil @@ -71,13 +71,13 @@ func (reg *resourceRegistry) ToLDAP(rsrc *accountserver.Resource) (attrs []ldap. return } -func setCommonResourceAttrs(entry *ldap.Entry, rsrc *accountserver.Resource) { +func setCommonResourceAttrs(entry *ldap.Entry, rsrc *as.Resource) { rsrc.Status = entry.GetAttributeValue("status") rsrc.Shard = entry.GetAttributeValue("host") rsrc.OriginalShard = entry.GetAttributeValue("originalHost") } -func (reg *resourceRegistry) FromLDAP(entry *ldap.Entry) (rsrc *accountserver.Resource, err error) { +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. for _, h := range reg.handlers { @@ -90,7 +90,7 @@ func (reg *resourceRegistry) FromLDAP(entry *ldap.Entry) (rsrc *accountserver.Re return nil, errors.New("unknown resource") } -func (reg *resourceRegistry) FromLDAPWithType(rsrcType string, entry *ldap.Entry) (rsrc *accountserver.Resource, err error) { +func (reg *resourceRegistry) FromLDAPWithType(rsrcType string, entry *ldap.Entry) (rsrc *as.Resource, err error) { err = reg.dispatch(rsrcType, func(h resourceHandler) (rerr error) { rsrc, rerr = h.FromLDAP(entry) if rerr != nil { @@ -131,7 +131,7 @@ type emailResourceHandler struct { baseDN string } -func (h *emailResourceHandler) GetDN(id accountserver.ResourceID) (string, error) { +func (h *emailResourceHandler) GetDN(id as.ResourceID) (string, error) { if id.User() == "" { return "", errors.New("unqualified resource id") } @@ -144,7 +144,7 @@ func (h *emailResourceHandler) GetDN(id accountserver.ResourceID) (string, error var errWrongObjectClass = errors.New("objectClass does not match") -func (h *emailResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Resource, error) { +func (h *emailResourceHandler) FromLDAP(entry *ldap.Entry) (*as.Resource, error) { if !isObjectClass(entry, "virtualMailUser") { return nil, errWrongObjectClass } @@ -155,22 +155,22 @@ func (h *emailResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Resou return nil, err } - return &accountserver.Resource{ - ID: accountserver.NewResourceID( - accountserver.ResourceTypeEmail, + return &as.Resource{ + ID: as.NewResourceID( + as.ResourceTypeEmail, username, email, ), - Type: accountserver.ResourceTypeEmail, + Type: as.ResourceTypeEmail, Name: email, - Email: &accountserver.Email{ + Email: &as.Email{ Aliases: entry.GetAttributeValues("mailAlternateAddr"), Maildir: entry.GetAttributeValue("mailMessageStore"), }, }, nil } -func (h *emailResourceHandler) ToLDAP(rsrc *accountserver.Resource) []ldap.PartialAttribute { +func (h *emailResourceHandler) ToLDAP(rsrc *as.Resource) []ldap.PartialAttribute { return []ldap.PartialAttribute{ {Type: "objectClass", Vals: []string{"top", "virtualMailUser"}}, {Type: "mail", Vals: s2l(rsrc.ID.Name())}, @@ -192,31 +192,31 @@ type mailingListResourceHandler struct { baseDN string } -func (h *mailingListResourceHandler) GetDN(id accountserver.ResourceID) (string, error) { +func (h *mailingListResourceHandler) GetDN(id as.ResourceID) (string, error) { dn := replaceVars("listName=${resource},ou=Lists", map[string]string{ "resource": id.Name(), }) return joinDN(dn, h.baseDN), nil } -func (h *mailingListResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Resource, error) { +func (h *mailingListResourceHandler) FromLDAP(entry *ldap.Entry) (*as.Resource, error) { if !isObjectClass(entry, "mailingList") { return nil, errWrongObjectClass } listName := entry.GetAttributeValue("listName") - return &accountserver.Resource{ - ID: accountserver.NewResourceID(accountserver.ResourceTypeMailingList, listName), - Type: accountserver.ResourceTypeMailingList, + return &as.Resource{ + ID: as.NewResourceID(as.ResourceTypeMailingList, listName), + Type: as.ResourceTypeMailingList, Name: listName, - List: &accountserver.MailingList{ + List: &as.MailingList{ Public: s2b(entry.GetAttributeValue("public")), Admins: entry.GetAttributeValues("listOwner"), }, }, nil } -func (h *mailingListResourceHandler) ToLDAP(rsrc *accountserver.Resource) []ldap.PartialAttribute { +func (h *mailingListResourceHandler) ToLDAP(rsrc *as.Resource) []ldap.PartialAttribute { return []ldap.PartialAttribute{ {Type: "objectClass", Vals: []string{"top", "mailingList"}}, {Type: "listName", Vals: s2l(rsrc.ID.Name())}, @@ -238,7 +238,7 @@ type websiteResourceHandler struct { baseDN string } -func (h *websiteResourceHandler) GetDN(id accountserver.ResourceID) (string, error) { +func (h *websiteResourceHandler) GetDN(id as.ResourceID) (string, error) { if id.User() == "" { return "", errors.New("unqualified resource id") } @@ -250,7 +250,7 @@ func (h *websiteResourceHandler) GetDN(id accountserver.ResourceID) (string, err return joinDN(dn, h.baseDN), nil } -func (h *websiteResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Resource, error) { +func (h *websiteResourceHandler) FromLDAP(entry *ldap.Entry) (*as.Resource, error) { if !isObjectClass(entry, "subSite") { return nil, errWrongObjectClass } @@ -259,21 +259,21 @@ func (h *websiteResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Res parentSite := entry.GetAttributeValue("parentSite") name := fmt.Sprintf("%s/%s", parentSite, alias) url := fmt.Sprintf("https://www.%s/%s/", parentSite, alias) - uid, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) + uid, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) // nolint username, err := getParentRDN(entry.DN, "uid") if err != nil { return nil, err } - return &accountserver.Resource{ - ID: accountserver.NewResourceID( - accountserver.ResourceTypeWebsite, + return &as.Resource{ + ID: as.NewResourceID( + as.ResourceTypeWebsite, username, alias, ), - Type: accountserver.ResourceTypeWebsite, + Type: as.ResourceTypeWebsite, Name: name, - Website: &accountserver.Website{ + Website: &as.Website{ URL: url, UID: uid, ParentDomain: parentSite, @@ -284,7 +284,7 @@ func (h *websiteResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Res }, nil } -func (h *websiteResourceHandler) ToLDAP(rsrc *accountserver.Resource) []ldap.PartialAttribute { +func (h *websiteResourceHandler) ToLDAP(rsrc *as.Resource) []ldap.PartialAttribute { return []ldap.PartialAttribute{ {Type: "objectClass", Vals: []string{"top", "subSite"}}, {Type: "alias", Vals: s2l(rsrc.ID.Name())}, @@ -308,7 +308,7 @@ type domainResourceHandler struct { baseDN string } -func (h *domainResourceHandler) GetDN(id accountserver.ResourceID) (string, error) { +func (h *domainResourceHandler) GetDN(id as.ResourceID) (string, error) { if id.User() == "" { return "", errors.New("unqualified resource id") } @@ -320,7 +320,7 @@ func (h *domainResourceHandler) GetDN(id accountserver.ResourceID) (string, erro return joinDN(dn, h.baseDN), nil } -func (h *domainResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Resource, error) { +func (h *domainResourceHandler) FromLDAP(entry *ldap.Entry) (*as.Resource, error) { if !isObjectClass(entry, "virtualHost") { return nil, errWrongObjectClass } @@ -330,17 +330,17 @@ func (h *domainResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Reso if err != nil { return nil, err } - uid, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) + uid, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) // nolint - return &accountserver.Resource{ - ID: accountserver.NewResourceID( - accountserver.ResourceTypeDomain, + return &as.Resource{ + ID: as.NewResourceID( + as.ResourceTypeDomain, username, cn, ), - Type: accountserver.ResourceTypeDomain, + Type: as.ResourceTypeDomain, Name: cn, - Website: &accountserver.Website{ + Website: &as.Website{ URL: fmt.Sprintf("https://%s/", cn), UID: uid, Options: entry.GetAttributeValues("option"), @@ -350,7 +350,7 @@ func (h *domainResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Reso }, nil } -func (h *domainResourceHandler) ToLDAP(rsrc *accountserver.Resource) []ldap.PartialAttribute { +func (h *domainResourceHandler) ToLDAP(rsrc *as.Resource) []ldap.PartialAttribute { return []ldap.PartialAttribute{ {Type: "objectClass", Vals: []string{"top", "virtualHost"}}, {Type: "cn", Vals: s2l(rsrc.ID.Name())}, @@ -373,7 +373,7 @@ type webdavResourceHandler struct { baseDN string } -func (h *webdavResourceHandler) GetDN(id accountserver.ResourceID) (string, error) { +func (h *webdavResourceHandler) GetDN(id as.ResourceID) (string, error) { if id.User() == "" { return "", errors.New("unqualified resource id") } @@ -385,7 +385,7 @@ func (h *webdavResourceHandler) GetDN(id accountserver.ResourceID) (string, erro return joinDN(dn, h.baseDN), nil } -func (h *webdavResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Resource, error) { +func (h *webdavResourceHandler) FromLDAP(entry *ldap.Entry) (*as.Resource, error) { if !isObjectClass(entry, "ftpAccount") { return nil, errWrongObjectClass } @@ -395,23 +395,23 @@ func (h *webdavResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Reso if err != nil { return nil, err } - uid, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) - return &accountserver.Resource{ - ID: accountserver.NewResourceID( - accountserver.ResourceTypeDAV, + uid, _ := strconv.Atoi(entry.GetAttributeValue(uidNumberLDAPAttr)) // nolint + return &as.Resource{ + ID: as.NewResourceID( + as.ResourceTypeDAV, username, name, ), - Type: accountserver.ResourceTypeDAV, + Type: as.ResourceTypeDAV, Name: name, - DAV: &accountserver.WebDAV{ + DAV: &as.WebDAV{ Homedir: entry.GetAttributeValue("homeDirectory"), UID: uid, }, }, nil } -func (h *webdavResourceHandler) ToLDAP(rsrc *accountserver.Resource) []ldap.PartialAttribute { +func (h *webdavResourceHandler) ToLDAP(rsrc *as.Resource) []ldap.PartialAttribute { return []ldap.PartialAttribute{ {Type: "objectClass", Vals: []string{"top", "person", "posixAccount", "shadowAccount", "organizationalPerson", "inetOrgPerson", "ftpAccount"}}, {Type: "ftpname", Vals: s2l(rsrc.ID.Name())}, @@ -438,7 +438,7 @@ type databaseResourceHandler struct { baseDN string } -func makeDatabaseResourceID(dn string) (rsrcID, parentID accountserver.ResourceID, err error) { +func makeDatabaseResourceID(dn string) (rsrcID, parentID as.ResourceID, err error) { parsed, perr := ldap.ParseDN(dn) if perr != nil { err = perr @@ -457,17 +457,17 @@ func makeDatabaseResourceID(dn string) (rsrcID, parentID accountserver.ResourceI // The username is the 3rd component. username := parsed.RDNs[2].Attributes[0].Value - rsrcID = accountserver.NewResourceID( - accountserver.ResourceTypeDatabase, + rsrcID = as.NewResourceID( + as.ResourceTypeDatabase, username, encParent, dbname, ) - var parentType = accountserver.ResourceTypeWebsite + var parentType = as.ResourceTypeWebsite if parsed.RDNs[1].Attributes[0].Type == "cn" { - parentType = accountserver.ResourceTypeDomain + parentType = as.ResourceTypeDomain } - parentID = accountserver.NewResourceID( + parentID = as.NewResourceID( parentType, username, parentName, @@ -475,7 +475,7 @@ func makeDatabaseResourceID(dn string) (rsrcID, parentID accountserver.ResourceI return } -func (h *databaseResourceHandler) GetDN(id accountserver.ResourceID) (string, error) { +func (h *databaseResourceHandler) GetDN(id as.ResourceID) (string, error) { if id.User() == "" || len(id.Parts) < 4 { return "", errors.New("unqualified resource id") } @@ -497,7 +497,7 @@ func (h *databaseResourceHandler) GetDN(id accountserver.ResourceID) (string, er return joinDN(dn, h.baseDN), nil } -func (h *databaseResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Resource, error) { +func (h *databaseResourceHandler) FromLDAP(entry *ldap.Entry) (*as.Resource, error) { if !isObjectClass(entry, "dbMysql") { return nil, errWrongObjectClass } @@ -507,19 +507,19 @@ func (h *databaseResourceHandler) FromLDAP(entry *ldap.Entry) (*accountserver.Re if err != nil { return nil, err } - return &accountserver.Resource{ + return &as.Resource{ ID: rsrcID, - Type: accountserver.ResourceTypeDatabase, + Type: as.ResourceTypeDatabase, ParentID: parentID, Name: name, - Database: &accountserver.Database{ + Database: &as.Database{ DBUser: entry.GetAttributeValue("dbuser"), CleartextPassword: entry.GetAttributeValue("clearPassword"), }, }, nil } -func (h *databaseResourceHandler) ToLDAP(rsrc *accountserver.Resource) []ldap.PartialAttribute { +func (h *databaseResourceHandler) ToLDAP(rsrc *as.Resource) []ldap.PartialAttribute { return []ldap.PartialAttribute{ {Type: "objectClass", Vals: []string{"top", "dbMysql"}}, {Type: "dbname", Vals: s2l(rsrc.ID.Name())}, diff --git a/backend/resources_test.go b/backend/resources_test.go index 362c09d620855032c9331c79b2e0172bc130f444..4f4d4bb6ce84738a8124ec4250567810f72e5c5f 100644 --- a/backend/resources_test.go +++ b/backend/resources_test.go @@ -6,7 +6,7 @@ import ( "github.com/go-test/deep" "gopkg.in/ldap.v2" - "git.autistici.org/ai3/accountserver" + as "git.autistici.org/ai3/accountserver" ) func TestEmailResource_FromLDAP(t *testing.T) { @@ -24,21 +24,21 @@ func TestEmailResource_FromLDAP(t *testing.T) { ) reg := newResourceRegistry() - reg.register(accountserver.ResourceTypeEmail, &emailResourceHandler{baseDN: "dc=example,dc=com"}) + reg.register(as.ResourceTypeEmail, &emailResourceHandler{baseDN: "dc=example,dc=com"}) r, err := reg.FromLDAP(entry) if err != nil { t.Fatal("FromLDAP", err) } - expected := &accountserver.Resource{ - ID: accountserver.NewResourceID("email", "test@investici.org", "test@investici.org"), + expected := &as.Resource{ + ID: as.NewResourceID("email", "test@investici.org", "test@investici.org"), Type: "email", Name: "test@investici.org", Status: "active", Shard: "host1", OriginalShard: "host1", - Email: &accountserver.Email{ + Email: &as.Email{ Aliases: []string{"test2@investici.org", "test3@investici.org"}, Maildir: "test/store", },