Skip to content

Commit 1c446d7

Browse files
Merge pull request #68 from adorton-adobe/feature/email-username
Update username/email/domain validation
2 parents 6e19413 + 971ef61 commit 1c446d7

File tree

3 files changed

+80
-88
lines changed

3 files changed

+80
-88
lines changed

tests/test_functional.py

Lines changed: 65 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -51,23 +51,6 @@ def test_user_adobeid_unicode():
5151
"useAdobeID": True}
5252

5353

54-
def test_user_adobeid_unicode_error_unicode_dot_above():
55-
with pytest.raises(ValueError) as excinfo:
56-
UserAction(email=u"lwałę[email protected]")
57-
assert excinfo.type == ArgumentError
58-
if six.PY2:
59-
assert excinfo.match(u"lwałę[email protected]".encode('utf8'))
60-
with pytest.raises(ValueError) as excinfo:
61-
UserAction(email=u"lwałę[email protected]".encode('utf8'))
62-
assert excinfo.type == ArgumentError
63-
assert excinfo.match(u"lwałę[email protected]".encode('utf8'))
64-
65-
66-
def test_user_adobeid_unicode_error_trailing_dot():
67-
with pytest.raises(ValueError):
68-
UserAction(email=u"[email protected]")
69-
70-
7154
def test_user_enterpriseid():
7255
user = UserAction(id_type=IdentityTypes.enterpriseID, email="[email protected]")
7356
assert user.wire_dict() == {"do": [], "user": "[email protected]"}
@@ -88,11 +71,6 @@ def test_user_federatedid_username():
8871
assert user.wire_dict() == {"do": [], "user": "dbrotsky", "domain": "k.on-the-side.net"}
8972

9073

91-
def test_user_federatedid_username_unicode_error():
92-
with pytest.raises(ValueError):
93-
UserAction(id_type=IdentityTypes.federatedID, username=u"lwałęsa", domain="k.on-the-side.net")
94-
95-
9674
def test_create_user_adobeid():
9775
user = UserAction(email="[email protected]")
9876
user.create()
@@ -122,43 +100,55 @@ def test_create_user_enterpriseid():
122100

123101

124102
def test_create_user_federatedid():
125-
user = UserAction(id_type=IdentityTypes.federatedID, email="[email protected]")
126-
user.create(first_name="Daniel", last_name="Brotsky", country="US")
127-
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": "[email protected]",
128-
"firstname": "Daniel", "lastname": "Brotsky",
103+
"""
104+
Test federated ID action with email address and no username
105+
"""
106+
user = UserAction(id_type=IdentityTypes.federatedID, email="[email protected]")
107+
user.create(first_name="Example", last_name="User", country="US")
108+
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": "[email protected]",
109+
"firstname": "Example", "lastname": "User",
129110
"country": "US",
130111
"option": "ignoreIfAlreadyExists"}}],
131-
"user": "[email protected]"}
112+
"user": "[email protected]"}
132113

133114

134-
def test_create_user_federated_id_unicode():
135-
user = UserAction(id_type=IdentityTypes.federatedID, email=u"[email protected]")
136-
user.create(first_name="Lech", last_name=u"Wałęsa", country="PL")
137-
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": u"[email protected]",
138-
"firstname": "Lech", "lastname": u"Wałęsa",
115+
def test_create_user_federatedid_unicode():
116+
"""
117+
Test federated ID action with email address and no username and unicode in at least one attribute
118+
"""
119+
user = UserAction(id_type=IdentityTypes.federatedID, email=u"[email protected]")
120+
user.create(first_name=u"Exampłę", last_name="User", country="PL")
121+
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": u"[email protected]",
122+
"firstname": u"Exampłę", "lastname": "User",
139123
"country": "PL",
140124
"option": "ignoreIfAlreadyExists"}}],
141-
"user": u"[email protected]"}
125+
"user": u"[email protected]"}
142126

143127

144128
def test_create_user_federatedid_username():
145-
user = UserAction(id_type=IdentityTypes.federatedID, username="dbrotsky", domain="k.on-the-side.net")
146-
user.create(first_name="Daniel", last_name="Brotsky", country="US", email="[email protected]")
147-
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": "[email protected]",
148-
"firstname": "Daniel", "lastname": "Brotsky",
129+
"""
130+
Test federated ID with a username (non-email format)
131+
"""
132+
user = UserAction(id_type=IdentityTypes.federatedID, username="user", domain="example.com")
133+
user.create(first_name="Example", last_name="User", country="US", email="[email protected]")
134+
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": "[email protected]",
135+
"firstname": "Example", "lastname": "User",
149136
"country": "US",
150137
"option": "ignoreIfAlreadyExists"}}],
151-
"user": "dbrotsky", "domain": "k.on-the-side.net"}
138+
"user": "user", "domain": "example.com"}
152139

153140

154141
def test_create_user_federatedid_username_unicode():
155-
user = UserAction(id_type=IdentityTypes.federatedID, username=u"lwalesa", domain="k.on-the-side.net")
156-
user.create(first_name="Lech", last_name=u"Wałęsa", country="PL", email=u"[email protected]")
157-
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": u"[email protected]",
158-
"firstname": "Lech", "lastname": u"Wałęsa",
142+
"""
143+
Test federated ID with a username and unicode attributes (non-email format)
144+
"""
145+
user = UserAction(id_type=IdentityTypes.federatedID, username=u"user", domain="example.com")
146+
user.create(first_name=u"Exampłę", last_name="User", country="PL", email=u"[email protected]")
147+
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": u"[email protected]",
148+
"firstname": u"Exampłę", "lastname": "User",
159149
"country": "PL",
160150
"option": "ignoreIfAlreadyExists"}}],
161-
"user": u"lwalesa", "domain": "k.on-the-side.net"}
151+
"user": u"user", "domain": "example.com"}
162152

163153

164154
def test_create_user_federatedid_username_email():
@@ -172,11 +162,40 @@ def test_create_user_federatedid_username_email():
172162
"user": "dbrotsky", "domain": "k.on-the-side.net"}
173163

174164

175-
def test_create_user_federatedid_username_mismatch():
165+
def test_create_user_federatedid_email_format_username():
166+
"""
167+
Create federated ID with username in email format (with email address)
168+
:return:
169+
"""
176170
user = UserAction(id_type=IdentityTypes.federatedID, username="dbrotsky", domain="k.on-the-side.net",
177-
171+
172+
user.create(first_name="Daniel", last_name="Brotsky", country="US")
173+
assert user.wire_dict() == {"do": [{"createFederatedID": {"email": "[email protected]",
174+
"firstname": "Daniel", "lastname": "Brotsky",
175+
"country": "US",
176+
"option": "ignoreIfAlreadyExists"}}],
177+
"user": "dbrotsky", "domain": "k.on-the-side.net"}
178+
179+
180+
def test_create_user_federatedid_username_mismatch():
181+
"""
182+
Mismatched email in UserAction constructor vs create()
183+
"""
184+
user = UserAction(id_type=IdentityTypes.federatedID, username="user", domain="example.com",
185+
178186
with pytest.raises(ValueError):
179-
user.create(first_name="Daniel", last_name="Brotsky", country="US", email="[email protected]")
187+
user.create(first_name="Example", last_name="User", country="US", email="[email protected]")
188+
189+
190+
def test_different_email_username():
191+
"""
192+
Update a user record so the email address is different than email-type username (federated only)
193+
"""
194+
user = UserAction(id_type=IdentityTypes.federatedID, email="[email protected]")
195+
user.update(email="[email protected]", username="[email protected]")
196+
assert user.wire_dict() == {"do": [{"update": {"email": "[email protected]",
197+
"username": "[email protected]"}}],
198+
"user": "[email protected]"}
180199

181200

182201
def test_update_user_adobeid():

umapi_client/functional.py

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -55,35 +55,6 @@ class UserAction(Action):
5555
A sequence of commands to perform on a single user.
5656
"""
5757

58-
# regex patterns to be compiled once and used in _validate
59-
# The RFC6531 extended syntax for email addresses allows Unicode alphanumerics in the local part,
60-
# but the Adobe identity system doesn't support that extended syntax for user account emails.
61-
# This is the RFC-allowed pattern:
62-
# _atext_pattern = r"(\w|[!#$%&'*+/=?^_`{|}~;-])+"
63-
# This is the one allowed by Adobe:
64-
_atext_pattern = r"[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+"
65-
_local_pattern = _atext_pattern + r"([.]" + _atext_pattern + ")*"
66-
_dns_pattern = r"[a-zA-Z0-9-]+([.][a-zA-Z0-9-]+)+"
67-
_email_regex = re.compile(r"\A" + _local_pattern + r"@" + _dns_pattern + r"\Z", re.UNICODE)
68-
_username_regex = re.compile(r"^" + _local_pattern + r"$", re.UNICODE)
69-
_domain_regex = re.compile(r"^" + _dns_pattern + r"$", re.UNICODE)
70-
71-
@classmethod
72-
def _validate(cls, email=None, username=None, domain=None):
73-
"""
74-
Validates the specified user attributes against their specifications.
75-
Input values must be strings (standard or unicode). Throws ArgumentError if any input is invalid
76-
:param email: an email address
77-
:param username: a username
78-
:param domain: a domain
79-
"""
80-
if email and not cls._email_regex.match(email):
81-
raise ArgumentError("'%s': Illegal email format (must be ascii, unquoted, with no comment part)" % (email,))
82-
if domain and not cls._domain_regex.match(domain):
83-
raise ArgumentError("'%s': Illegal domain format" % (domain,))
84-
if username and not cls._username_regex.match(username):
85-
raise ArgumentError("'%s': Illegal username format (must be unquoted email local part)" % (username,))
86-
8758
def __init__(self, id_type=IdentityTypes.adobeID, email=None, username=None, domain=None, **kwargs):
8859
"""
8960
Create an Action for a user identified either by email or by username and domain.
@@ -112,27 +83,27 @@ def __init__(self, id_type=IdentityTypes.adobeID, email=None, username=None, dom
11283
self.id_type = id_type
11384
self.email = None
11485
self.domain = None
115-
if username:
86+
if username is not None:
11687
if email and username.lower() == email.lower():
11788
# ignore the username if it's the same as the email (policy default)
11889
username = None
11990
elif id_type is not IdentityTypes.federatedID:
12091
raise ArgumentError("Username must match email except for Federated ID")
12192
else:
122-
self._validate(username=username)
12393
if domain:
124-
self._validate(domain=domain)
12594
self.domain = domain
126-
if email:
127-
self._validate(email=email)
95+
if email is not None:
96+
if '@' not in email:
97+
raise ArgumentError("Invalid email address: %s" % email)
12898
self.email = email
12999
if not self.domain:
130100
atpos = email.index('@')
131101
self.domain = email[atpos + 1:]
132102
elif not username:
133-
raise ArgumentError("No user identity specified.")
103+
raise ArgumentError("No user identity specified.")
134104
elif not domain:
135105
raise ArgumentError("Both username and domain must be specified")
106+
136107
if username:
137108
Action.__init__(self, user=username, domain=self.domain, **kwargs)
138109
elif id_type == IdentityTypes.adobeID:
@@ -162,7 +133,6 @@ def create(self, first_name=None, last_name=None, country=None, email=None,
162133
if not self.email:
163134
raise ArgumentError("You must specify email when creating a user")
164135
elif self.email is None:
165-
self._validate(email=email)
166136
self.email = email
167137
elif self.email.lower() != email.lower():
168138
raise ArgumentError("Specified email (%s) doesn't match user's email (%s)" % (email, self.email))
@@ -195,15 +165,18 @@ def update(self, email=None, username=None, first_name=None, last_name=None, cou
195165
:param country: new country for this user
196166
:return: the User, so you can do User(...).update(...).add_to_groups(...)
197167
"""
198-
if email:
199-
self._validate(email=email)
200-
if username:
201-
self._validate(username=username)
168+
if username and self.id_type != IdentityTypes.federatedID:
169+
raise ArgumentError("You cannot set username except for a federated ID")
170+
if username and '@' in username and not email:
171+
raise ArgumentError("Cannot update email-type username when email is not specified")
172+
if email and username and email.lower() == username.lower():
173+
raise ArgumentError("Specify just email to set both email and username for a federated ID")
202174
updates = {}
203175
for k, v in six.iteritems(dict(email=email, username=username,
204176
firstname=first_name, lastname=last_name,
205177
country=country)):
206-
if v: updates[k] = v
178+
if v:
179+
updates[k] = v
207180
return self.append(update=updates)
208181

209182
def add_to_groups(self, groups=None, all_groups=False, group_type=None):

umapi_client/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@
1818
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1919
# SOFTWARE.
2020

21-
__version__ = "2.11"
21+
__version__ = "2.12"

0 commit comments

Comments
 (0)