summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAbhilash Raj2015-03-21 00:32:10 +0530
committerAbhilash Raj2015-03-21 00:32:10 +0530
commitae2a7c9a22f5b6eeed1a6884c6dcd87ed9ba673d (patch)
tree270cef8ecb93fba8eaee33381f1415c54fd7af60 /src
parent6280c5ffcd2fdebf80f170f7c9a4e47adf0c6c4a (diff)
downloadmailman-ae2a7c9a22f5b6eeed1a6884c6dcd87ed9ba673d.tar.gz
mailman-ae2a7c9a22f5b6eeed1a6884c6dcd87ed9ba673d.tar.zst
mailman-ae2a7c9a22f5b6eeed1a6884c6dcd87ed9ba673d.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/app/registrar.py2
-rw-r--r--src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py45
-rw-r--r--src/mailman/model/domain.py39
-rw-r--r--src/mailman/model/user.py11
-rw-r--r--src/mailman/rest/domains.py21
-rw-r--r--src/mailman/rest/users.py36
6 files changed, 128 insertions, 26 deletions
diff --git a/src/mailman/app/registrar.py b/src/mailman/app/registrar.py
index 252a7eb9b..f601b645a 100644
--- a/src/mailman/app/registrar.py
+++ b/src/mailman/app/registrar.py
@@ -161,8 +161,6 @@ def handle_ConfirmationNeededEvent(event):
# For i18n interpolation.
confirm_url = mlist.domain.confirm_url(event.token)
email_address = event.pendable['email']
- domain_name = mlist.domain.mail_host
- contact_address = mlist.domain.contact_address
# Send a verification email to the address.
template = getUtility(ITemplateLoader).get(
'mailman:///{0}/{1}/confirm.txt'.format(
diff --git a/src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py b/src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py
new file mode 100644
index 000000000..bf42e2232
--- /dev/null
+++ b/src/mailman/database/alembic/versions/46e92facee7_add_serverowner_domainowner.py
@@ -0,0 +1,45 @@
+"""add_serverowner_domainowner
+
+Revision ID: 46e92facee7
+Revises: 33e1f5f6fa8
+Create Date: 2015-03-20 16:01:25.007242
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '46e92facee7'
+down_revision = '33e1f5f6fa8'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+def upgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.create_table('owner',
+ sa.Column('user_id', sa.Integer(), nullable=False),
+ sa.Column('domain_id', sa.Integer(), nullable=False),
+ sa.ForeignKeyConstraint(['domain_id'], ['domain.id'], ),
+ sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
+ sa.PrimaryKeyConstraint('user_id', 'domain_id')
+ )
+ op.add_column('user', sa.Column('is_serverowner', sa.Boolean(),
+ nullable=True))
+ if op.get_bind().dialect.name != 'sqlite':
+ op.drop_column('domain', 'contact_address')
+ # The migration below may be because the first migration was created
+ # before we changed this attribute to a primary_key
+ op.create_foreign_key('_preferred_address', 'user', 'address',
+ ['_preferred_address_id'], ['id'])
+ ### end Alembic commands ###
+
+
+def downgrade():
+ ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('user', 'is_serverowner')
+ if op.get_bind().dialect.name != 'sqlite':
+ op.add_column('domain', sa.Column('contact_address', sa.VARCHAR(),
+ nullable=True))
+ op.drop_constraint('_preferred_address', 'user', type_='foreignkey')
+ op.drop_table('owner')
+ ### end Alembic commands ###
diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py
index 9e627c119..b6705a619 100644
--- a/src/mailman/model/domain.py
+++ b/src/mailman/model/domain.py
@@ -29,8 +29,10 @@ from mailman.interfaces.domain import (
BadDomainSpecificationError, DomainCreatedEvent, DomainCreatingEvent,
DomainDeletedEvent, DomainDeletingEvent, IDomain, IDomainManager)
from mailman.model.mailinglist import MailingList
+from mailman.model.user import User
from urllib.parse import urljoin, urlparse
from sqlalchemy import Column, Integer, Unicode
+from sqlalchemy.orm import relationship, backref
from zope.event import notify
from zope.interface import implementer
@@ -47,12 +49,14 @@ class Domain(Model):
mail_host = Column(Unicode) # TODO: add index?
base_url = Column(Unicode)
description = Column(Unicode)
- contact_address = Column(Unicode)
+ owners = relationship("User",
+ secondary="owner",
+ backref="domains")
def __init__(self, mail_host,
description=None,
base_url=None,
- contact_address=None):
+ owner=None):
"""Create and register a domain.
:param mail_host: The host name for the email interface.
@@ -63,18 +67,16 @@ class Domain(Model):
scheme. If not given, it will be constructed from the
`mail_host` using the http protocol.
:type base_url: string
- :param contact_address: The email address to contact a human for this
- domain. If not given, postmaster@`mail_host` will be used.
- :type contact_address: string
+ :param owner: The `User` who is the owner of this domain
+ :type owner: mailman.models.user.User
"""
self.mail_host = mail_host
self.base_url = (base_url
if base_url is not None
else 'http://' + mail_host)
self.description = description
- self.contact_address = (contact_address
- if contact_address is not None
- else 'postmaster@' + mail_host)
+ if owner is not None:
+ self.owners.append(owner)
@property
def url_host(self):
@@ -103,14 +105,14 @@ class Domain(Model):
def __repr__(self):
"""repr(a_domain)"""
if self.description is None:
- return ('<Domain {0.mail_host}, base_url: {0.base_url}, '
- 'contact_address: {0.contact_address}>').format(self)
+ return ('<Domain {0.mail_host}, base_url: {0.base_url}').format(self)
else:
return ('<Domain {0.mail_host}, {0.description}, '
- 'base_url: {0.base_url}, '
- 'contact_address: {0.contact_address}>').format(self)
-
+ 'base_url: {0.base_url}, ').format(self)
+ def add_owner(self, owner):
+ """ Add an owner to a domain"""
+ self.owners.append(owner)
@implementer(IDomainManager)
class DomainManager:
@@ -121,15 +123,22 @@ class DomainManager:
mail_host,
description=None,
base_url=None,
- contact_address=None):
+ owner_id=None):
"""See `IDomainManager`."""
# Be sure the mail_host is not already registered. This is probably
# a constraint that should (also) be maintained in the database.
if self.get(mail_host) is not None:
raise BadDomainSpecificationError(
'Duplicate email host: %s' % mail_host)
+ # Be sure that the owner exists
+ owner = None
+ if owner_id is not None:
+ owner = store.query(User).filter(id==owner_id).first()
+ if owner is None:
+ raise BadDomainSpecificationError(
+ 'Owner does not exist')
notify(DomainCreatingEvent(mail_host))
- domain = Domain(mail_host, description, base_url, contact_address)
+ domain = Domain(mail_host, description, base_url, owner)
store.add(domain)
notify(DomainCreatedEvent(domain))
return domain
diff --git a/src/mailman/model/user.py b/src/mailman/model/user.py
index b74ea6d06..5ebe69a37 100644
--- a/src/mailman/model/user.py
+++ b/src/mailman/model/user.py
@@ -34,7 +34,7 @@ from mailman.model.preferences import Preferences
from mailman.model.roster import Memberships
from mailman.utilities.datetime import factory as date_factory
from mailman.utilities.uid import UniqueIDFactory
-from sqlalchemy import Column, DateTime, ForeignKey, Integer, Unicode
+from sqlalchemy import Column, DateTime, ForeignKey, Integer, Unicode, Boolean
from sqlalchemy.orm import relationship, backref
from zope.event import notify
from zope.interface import implementer
@@ -55,6 +55,7 @@ class User(Model):
_password = Column('password', Unicode)
_user_id = Column(UUID, index=True)
_created_on = Column(DateTime)
+ is_serverowner = Column(Boolean, default=False)
addresses = relationship(
'Address', backref='user',
@@ -174,3 +175,11 @@ class User(Model):
@property
def memberships(self):
return Memberships(self)
+
+
+class Owner(Model):
+ """Doomain to owners(user) association class"""
+
+ __tablename__ = 'owner'
+ user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
+ domain_id = Column(Integer, ForeignKey('domain.id'), primary_key=True)
diff --git a/src/mailman/rest/domains.py b/src/mailman/rest/domains.py
index 345e8327d..d312737df 100644
--- a/src/mailman/rest/domains.py
+++ b/src/mailman/rest/domains.py
@@ -29,6 +29,7 @@ from mailman.rest.helpers import (
BadRequest, CollectionMixin, NotFound, bad_request, child, created, etag,
no_content, not_found, okay, path_to)
from mailman.rest.lists import ListsForDomain
+from mailman.rest.users import OwnersForDomain
from mailman.rest.validator import Validator
from zope.component import getUtility
@@ -41,7 +42,6 @@ class _DomainBase(CollectionMixin):
"""See `CollectionMixin`."""
return dict(
base_url=domain.base_url,
- contact_address=domain.contact_address,
description=domain.description,
mail_host=domain.mail_host,
self_link=path_to('domains/{0}'.format(domain.mail_host)),
@@ -88,6 +88,17 @@ class ADomain(_DomainBase):
else:
return BadRequest(), []
+ @child()
+ def owners(self, request, segments):
+ """/domains/<domain>/owners"""
+ if len(segments) == 0:
+ domain = getUtility(IDomainManager).get(self._domain)
+ if domain is None:
+ return NotFound()
+ return OwnersForDomain(domain)
+ else:
+ return BadRequest(), []
+
class AllDomains(_DomainBase):
"""The domains."""
@@ -99,12 +110,12 @@ class AllDomains(_DomainBase):
validator = Validator(mail_host=str,
description=str,
base_url=str,
- contact_address=str,
+ owner_id=int,
_optional=('description', 'base_url',
- 'contact_address'))
+ 'owner_id'))
domain = domain_manager.add(**validator(request))
- except BadDomainSpecificationError:
- bad_request(response, b'Domain exists')
+ except BadDomainSpecificationError as error:
+ bad_request(response, str(error))
except ValueError as error:
bad_request(response, str(error))
else:
diff --git a/src/mailman/rest/users.py b/src/mailman/rest/users.py
index 018c8441d..b1aca9678 100644
--- a/src/mailman/rest/users.py
+++ b/src/mailman/rest/users.py
@@ -67,8 +67,9 @@ CREATION_FIELDS = dict(
email=str,
display_name=str,
password=str,
- _optional=('display_name', 'password'),
- )
+ is_serverowner=bool,
+ _optional=('display_name', 'password', 'is_serverowner'),
+)
@@ -108,7 +109,8 @@ class _UserBase(CollectionMixin):
user_id=user_id,
created_on=user.created_on,
self_link=path_to('users/{}'.format(user_id)),
- )
+ is_serverowner=user.is_serverowner,
+ )
# Add the password attribute, only if the user has a password. Same
# with the real name. These could be None or the empty string.
if user.password:
@@ -377,3 +379,31 @@ class Login:
no_content(response)
else:
forbidden(response)
+
+class OwnersForDomain(_UserBase):
+ """Owners for a particular domain."""
+
+ def __init__(self, domain):
+ self._domain = domain
+
+ def on_get(self, request, response):
+ """/domains/<domain>/owners"""
+ resource = self._make_collection(request)
+ okay(response, etag(resource))
+
+ def on_post(self, request, response):
+ """POST to /domains/<domain>/owners """
+ validator = Validator(owner_id=GetterSetter(int))
+ try:
+ values = validator(request)
+ except ValueError as error:
+ bad_request(response, str(error))
+ return
+ owner = getUtility(IUserManager).get_user_by_id(values['owner_id'])
+ self._domain.add_owner(owner)
+ return no_content(response)
+
+ @paginate
+ def _get_collection(self, request):
+ """See `CollectionMixin`."""
+ return list(self._domain.owners)