-
-
Notifications
You must be signed in to change notification settings - Fork 75
/
login.py
106 lines (89 loc) · 3.79 KB
/
login.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
from Acquisition import aq_inner
from Acquisition import aq_parent
from plone.restapi.deserializer import json_body
from plone.restapi.services import Service
from Products.CMFCore.utils import getToolByName
from Products.PluggableAuthService.interfaces.plugins import IAuthenticationPlugin
from zope import component
from zope.interface import alsoProvides
import plone.protect.interfaces
class Login(Service):
"""Handles login and returns a JSON web token (JWT)."""
def reply(self):
data = json_body(self.request)
if "login" not in data or "password" not in data:
self.request.response.setStatus(400)
return dict(
error=dict(
type="Missing credentials",
message="Login and password must be provided in body.",
)
)
# Disable CSRF protection
if "IDisableCSRFProtection" in dir(plone.protect.interfaces):
alsoProvides(self.request, plone.protect.interfaces.IDisableCSRFProtection)
userid = data["login"]
password = data["password"]
uf = self._find_userfolder(userid)
# Also put the password in __ac_password on the request.
# The post-login code in PlonePAS expects to find it there
# when it calls the PAS updateCredentials plugin.
self.request.form["__ac_password"] = data["password"]
if uf is not None:
plugins = uf._getOb("plugins")
authenticators = plugins.listPlugins(IAuthenticationPlugin)
plugin = None
for id_, authenticator in authenticators:
if authenticator.meta_type == "JWT Authentication Plugin":
plugin = authenticator
break
if plugin is None:
self.request.response.setStatus(501)
return dict(
error=dict(
type="Login failed",
message="JWT authentication plugin not installed.",
)
)
user = uf.authenticate(userid, password, self.request)
else:
user = None
if not user:
self.request.response.setStatus(401)
return dict(
error=dict(
type="Invalid credentials", message="Wrong login and/or password."
)
)
# Perform the same post-login actions as would happen when logging in through
# the Plone classic HTML login form. There is a trade-off here, we either
# violate DRY and duplicate the code from the classic HTML Plone view that will
# then become out of date all the time, or we re-use the code from the core
# Plone view and introduce a dependency we may have to update over time. After
# [discussion](https://github.com/plone/plone.restapi/pull/1141#discussion_r648843942)
# we opt for the latter.
login_view = component.getMultiAdapter(
(self.context, self.request),
name="login",
)
login_view._post_login()
payload = {}
payload["fullname"] = user.getProperty("fullname")
return {"token": plugin.create_token(user.getId(), data=payload)}
def _find_userfolder(self, userid):
"""Try to find a user folder that contains a user with the given
userid.
"""
uf_parent = aq_inner(self.context)
info = None
while not info:
uf = getToolByName(uf_parent, "acl_users")
if uf:
info = uf._verifyUser(uf.plugins, login=userid)
if uf_parent is self.context.getPhysicalRoot():
break
uf_parent = aq_parent(uf_parent)
if info:
return uf
def check_permission(self):
pass