user-modify.py 13.5 KB
Newer Older
1
# Copyright 2012, 2013, 2014 GRNET S.A. All rights reserved.
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
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
#
#   1. Redistributions of source code must retain the above
#      copyright notice, this list of conditions and the following
#      disclaimer.
#
#   2. Redistributions in binary form must reproduce the above
#      copyright notice, this list of conditions and the following
#      disclaimer in the documentation and/or other materials
#      provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#
# The views and conclusions contained in the software and
# documentation are those of the authors and should not be
# interpreted as representing official policies, either expressed
# or implied, of GRNET S.A.

34
import string
35
from datetime import datetime
36

37 38
from optparse import make_option

39
from django.core import management
40
from django.db import transaction
41
from django.core.management.base import BaseCommand, CommandError
42
from django.contrib.auth.models import Group
43
from django.core.exceptions import ValidationError
44
from django.core.validators import validate_email
45

46
from astakos.im.models import AstakosUser
47
from astakos.im import activation_backends
48
from ._common import (remove_user_permission, add_user_permission, is_uuid)
49 50

activation_backend = activation_backends.get_backend()
51

52

53
class Command(BaseCommand):
54
    args = "<user ID> (or --all)"
55
    help = "Modify a user's attributes"
56

Olga Brani's avatar
Olga Brani committed
57
    option_list = BaseCommand.option_list + (
58 59 60 61 62 63 64 65 66
        make_option('--all',
                    action='store_true',
                    default=False,
                    help=("Operate on all users. Currently only setting "
                          "base quota is supported in this mode. Can be "
                          "combined with `--exclude'.")),
        make_option('--exclude',
                    help=("If `--all' is given, exclude users given as a "
                          "list of uuids: uuid1,uuid2,uuid3")),
67
        make_option('--invitations',
68 69 70
                    dest='invitations',
                    metavar='NUM',
                    help="Update user's invitations"),
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
71
        make_option('--level',
72 73 74
                    dest='level',
                    metavar='NUM',
                    help="Update user's level"),
75
        make_option('--password',
76 77 78
                    dest='password',
                    metavar='PASSWORD',
                    help="Set user's password"),
79
        make_option('--renew-token',
80 81 82 83
                    action='store_true',
                    dest='renew_token',
                    default=False,
                    help="Renew the user's token"),
84
        make_option('--renew-password',
85 86 87 88
                    action='store_true',
                    dest='renew_password',
                    default=False,
                    help="Renew the user's password"),
89
        make_option('--set-admin',
90 91 92 93
                    action='store_true',
                    dest='admin',
                    default=False,
                    help="Give user admin rights"),
94
        make_option('--set-noadmin',
95 96 97 98
                    action='store_true',
                    dest='noadmin',
                    default=False,
                    help="Revoke user's admin rights"),
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
99
        make_option('--set-active',
100 101 102
                    action='store_true',
                    dest='active',
                    default=False,
103
                    help="Change user's state to active"),
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
104
        make_option('--set-inactive',
105 106 107 108
                    action='store_true',
                    dest='inactive',
                    default=False,
                    help="Change user's state to inactive"),
109 110 111
        make_option('--inactive-reason',
                    dest='inactive_reason',
                    help="Reason user got inactive"),
112
        make_option('--add-group',
113 114
                    dest='add-group',
                    help="Add user group"),
115
        make_option('--delete-group',
116 117
                    dest='delete-group',
                    help="Delete user group"),
118
        make_option('--add-permission',
119 120
                    dest='add-permission',
                    help="Add user permission"),
121
        make_option('--delete-permission',
122 123
                    dest='delete-permission',
                    help="Delete user permission"),
124 125 126 127
        make_option('--accept',
                    dest='accept',
                    action='store_true',
                    help="Accept user"),
128 129 130 131
        make_option('--verify',
                    dest='verify',
                    action='store_true',
                    help="Verify user email"),
132 133 134 135 136 137 138
        make_option('--reject',
                    dest='reject',
                    action='store_true',
                    help="Reject user"),
        make_option('--reject-reason',
                    dest='reject_reason',
                    help="Reason user got rejected"),
139 140 141 142
        make_option('--sign-terms',
                    default=False,
                    action='store_true',
                    help="Sign terms"),
143 144 145 146 147
        make_option('-f', '--no-confirm',
                    action='store_true',
                    default=False,
                    dest='force',
                    help="Do not ask for confirmation"),
148 149 150 151 152 153
        make_option('--set-email',
                    dest='set-email',
                    help="Change user's email"),
        make_option('--delete',
                    dest='delete',
                    action='store_true',
154
                    help="Delete a non-accepted user"),
Olga Brani's avatar
Olga Brani committed
155 156
    )

157
    @transaction.commit_on_success
158
    def handle(self, *args, **options):
159 160 161 162 163 164
        if options['all']:
            if not args:
                return self.handle_all_users(*args, **options)
            else:
                raise CommandError("Please provide a user ID or --all")

165
        if len(args) != 1:
166 167 168 169 170
            raise CommandError("Please provide a user ID or --all")

        if options["exclude"] is not None:
            m = "Option --exclude is meaningful only combined with --all."
            raise CommandError(m)
171

172
        if args[0].isdigit():
173
            try:
174 175
                user = AstakosUser.objects.select_for_update().\
                    get(id=int(args[0]))
176 177 178 179 180 181 182
            except AstakosUser.DoesNotExist:
                raise CommandError("Invalid user ID")
        elif is_uuid(args[0]):
            try:
                user = AstakosUser.objects.get(uuid=args[0])
            except AstakosUser.DoesNotExist:
                raise CommandError("Invalid user UUID")
183
        else:
184 185 186
            raise CommandError(("Invalid user identification: "
                                "you should provide a valid user ID "
                                "or a valid user UUID"))
187

188 189 190 191
        if options.get('admin'):
            user.is_superuser = True
        elif options.get('noadmin'):
            user.is_superuser = False
192

193 194 195 196 197 198 199 200 201 202 203 204
        if options.get('reject'):
            reject_reason = options.get('reject_reason', None)
            res = activation_backend.handle_moderation(
                user,
                accept=False,
                reject_reason=reject_reason)
            activation_backend.send_result_notifications(res, user)
            if res.is_error():
                print "Failed to reject.", res.message
            else:
                print "Account rejected"

205 206 207 208 209 210 211 212 213 214
        if options.get('verify'):
            res = activation_backend.handle_verification(
                user,
                user.verification_code)
            #activation_backend.send_result_notifications(res, user)
            if res.is_error():
                print "Failed to verify.", res.message
            else:
                print "Account verified (%s)" % res.status_display()

215 216 217 218 219 220 221 222
        if options.get('accept'):
            res = activation_backend.handle_moderation(user, accept=True)
            activation_backend.send_result_notifications(res, user)
            if res.is_error():
                print "Failed to accept.", res.message
            else:
                print "Account accepted and activated"

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
223
        if options.get('active'):
224 225 226 227 228 229
            res = activation_backend.activate_user(user)
            if res.is_error():
                print "Failed to activate.", res.message
            else:
                print "Account %s activated" % user.username

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
230
        elif options.get('inactive'):
231 232 233 234 235 236 237
            res = activation_backend.deactivate_user(
                user,
                reason=options.get('inactive_reason', None))
            if res.is_error():
                print "Failed to deactivate,", res.message
            else:
                print "Account %s deactivated" % user.username
238

239 240 241
        invitations = options.get('invitations')
        if invitations is not None:
            user.invitations = int(invitations)
242

243
        groupname = options.get('add-group')
Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
244 245
        if groupname is not None:
            try:
Olga Brani's avatar
Olga Brani committed
246 247 248
                group = Group.objects.get(name=groupname)
                user.groups.add(group)
            except Group.DoesNotExist, e:
249
                self.stderr.write(
250 251
                    "Group named %s does not exist\n" % groupname)

252 253 254
        groupname = options.get('delete-group')
        if groupname is not None:
            try:
Olga Brani's avatar
Olga Brani committed
255 256 257
                group = Group.objects.get(name=groupname)
                user.groups.remove(group)
            except Group.DoesNotExist, e:
258
                self.stderr.write(
259 260
                    "Group named %s does not exist\n" % groupname)

261 262 263 264 265
        pname = options.get('add-permission')
        if pname is not None:
            try:
                r, created = add_user_permission(user, pname)
                if created:
266
                    self.stderr.write(
267
                        'Permission: %s created successfully\n' % pname)
268
                if r > 0:
269
                    self.stderr.write(
270 271
                        'Permission: %s added successfully\n' % pname)
                elif r == 0:
272
                    self.stderr.write(
273
                        'User has already permission: %s\n' % pname)
274 275
            except Exception, e:
                raise CommandError(e)
276 277

        pname = options.get('delete-permission')
278 279 280 281
        if pname is not None and not user.has_perm(pname):
            try:
                r = remove_user_permission(user, pname)
                if r < 0:
282
                    self.stderr.write(
283
                        'Invalid permission codename: %s\n' % pname)
284
                elif r == 0:
285
                    self.stderr.write('User has not permission: %s\n' % pname)
286
                elif r > 0:
287
                    self.stderr.write(
288
                        'Permission: %s removed successfully\n' % pname)
289 290
            except Exception, e:
                raise CommandError(e)
291

Sofia Papagiannaki's avatar
Sofia Papagiannaki committed
292 293 294
        level = options.get('level')
        if level is not None:
            user.level = int(level)
295

296 297 298
        password = options.get('password')
        if password is not None:
            user.set_password(password)
299

300 301 302 303
        password = None
        if options['renew_password']:
            password = AstakosUser.objects.make_random_password()
            user.set_password(password)
304

305 306
        if options['renew_token']:
            user.renew_token()
307

308 309 310 311
        if options['sign_terms']:
            user.has_signed_terms = True
            user.date_signed_terms = datetime.now()

312 313 314
        try:
            user.save()
        except ValidationError, e:
315
            raise CommandError(e)
316

317 318
        if password:
            self.stdout.write('User\'s new password: %s\n' % password)
319

320
        force = options['force']
321 322
        delete = options.get('delete')
        if delete:
323 324 325
            if user.is_accepted():
                m = "Cannot delete. User %s is accepted." % user
                raise CommandError(m)
326 327 328
            management.call_command('user-show', str(user.pk),
                                    list_quotas=True)

329 330 331 332
            if not force:
                self.stdout.write("About to delete user %s. " % user.uuid)
                self.confirm()
            user.delete()
333 334 335 336 337 338 339 340 341 342 343 344 345 346 347

        # Change users email address
        newemail = options.get('set-email', None)
        if newemail is not None:
            newemail = newemail.strip()
            try:
                validate_email(newemail)
            except ValidationError:
                m = "Invalid email address."
                raise CommandError(m)

            if AstakosUser.objects.user_exists(newemail):
                m = "A user with this email address already exists."
                raise CommandError(m)

348
            user.set_email(newemail)
349 350
            user.save()

351 352
    def confirm(self):
        self.stdout.write("Confirm? [y/N] ")
353 354 355 356
        try:
            response = raw_input()
        except EOFError:
            response = "ABORT"
357
        if string.lower(response) not in ['y', 'yes']:
358
            self.stderr.write("Aborted.\n")
359 360 361
            exit()

    def handle_all_users(self, *args, **options):
362
        pass