Allow ASes to deactivate their own users

This commit is contained in:
Richard van der Hoff 2017-10-27 00:01:00 +01:00
parent 1ad1ba9e6a
commit 785bd7fd75
2 changed files with 33 additions and 17 deletions

View File

@ -82,7 +82,7 @@ class AuthHandler(BaseHandler):
def check_auth(self, flows, clientdict, clientip): def check_auth(self, flows, clientdict, clientip):
""" """
Takes a dictionary sent by the client in the login / registration Takes a dictionary sent by the client in the login / registration
protocol and handles the login flow. protocol and handles the User-Interactive Auth flow.
As a side effect, this function fills in the 'creds' key on the user's As a side effect, this function fills in the 'creds' key on the user's
session with a map, which maps each auth-type (str) to the relevant session with a map, which maps each auth-type (str) to the relevant

View File

@ -13,22 +13,21 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import logging
from twisted.internet import defer from twisted.internet import defer
from synapse.api.auth import has_access_token
from synapse.api.constants import LoginType from synapse.api.constants import LoginType
from synapse.api.errors import LoginError, SynapseError, Codes from synapse.api.errors import Codes, LoginError, SynapseError
from synapse.http.servlet import ( from synapse.http.servlet import (
RestServlet, parse_json_object_from_request, assert_params_in_request RestServlet, assert_params_in_request,
parse_json_object_from_request,
) )
from synapse.util.async import run_on_reactor from synapse.util.async import run_on_reactor
from synapse.util.msisdn import phone_number_to_msisdn from synapse.util.msisdn import phone_number_to_msisdn
from ._base import client_v2_patterns from ._base import client_v2_patterns
import logging
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -172,6 +171,18 @@ class DeactivateAccountRestServlet(RestServlet):
def on_POST(self, request): def on_POST(self, request):
body = parse_json_object_from_request(request) body = parse_json_object_from_request(request)
# if the caller provides an access token, it ought to be valid.
requester = None
if has_access_token(request):
requester = yield self.auth.get_user_by_req(
request,
) # type: synapse.types.Requester
# allow ASes to dectivate their own users
if requester and requester.app_service:
yield self._deactivate_account(requester.user.to_string())
defer.returnValue((200, {}))
authed, result, params, _ = yield self.auth_handler.check_auth([ authed, result, params, _ = yield self.auth_handler.check_auth([
[LoginType.PASSWORD], [LoginType.PASSWORD],
], body, self.hs.get_ip_from_request(request)) ], body, self.hs.get_ip_from_request(request))
@ -179,27 +190,32 @@ class DeactivateAccountRestServlet(RestServlet):
if not authed: if not authed:
defer.returnValue((401, result)) defer.returnValue((401, result))
user_id = None
requester = None
if LoginType.PASSWORD in result: if LoginType.PASSWORD in result:
user_id = result[LoginType.PASSWORD]
# if using password, they should also be logged in # if using password, they should also be logged in
requester = yield self.auth.get_user_by_req(request) if requester is None:
user_id = requester.user.to_string() raise SynapseError(
if user_id != result[LoginType.PASSWORD]: 400,
"Deactivate account requires an access_token",
errcode=Codes.MISSING_TOKEN
)
if requester.user.to_string() != user_id:
raise LoginError(400, "", Codes.UNKNOWN) raise LoginError(400, "", Codes.UNKNOWN)
else: else:
logger.error("Auth succeeded but no known type!", result.keys()) logger.error("Auth succeeded but no known type!", result.keys())
raise SynapseError(500, "", Codes.UNKNOWN) raise SynapseError(500, "", Codes.UNKNOWN)
# FIXME: Theoretically there is a race here wherein user resets password yield self._deactivate_account(user_id)
# using threepid. defer.returnValue((200, {}))
@defer.inlineCallbacks
def _deactivate_account(self, user_id):
# FIXME: Theoretically there is a race here wherein user resets
# password using threepid.
yield self.store.user_delete_access_tokens(user_id) yield self.store.user_delete_access_tokens(user_id)
yield self.store.user_delete_threepids(user_id) yield self.store.user_delete_threepids(user_id)
yield self.store.user_set_password_hash(user_id, None) yield self.store.user_set_password_hash(user_id, None)
defer.returnValue((200, {}))
class EmailThreepidRequestTokenRestServlet(RestServlet): class EmailThreepidRequestTokenRestServlet(RestServlet):
PATTERNS = client_v2_patterns("/account/3pid/email/requestToken$") PATTERNS = client_v2_patterns("/account/3pid/email/requestToken$")