~martinarrieta/mysql-fabric/BUG-72094

« back to all changes in this revision

Viewing changes to lib/mysql/fabric/credentials.py

  • Committer: Geert Vanderkelen
  • Date: 2014-03-07 06:36:46 UTC
  • Revision ID: geert.vanderkelen@oracle.com-20140307063646-pc6gqb0g6pevhe9s
Post-push fix for WL#7455.

* 'username' in section [protocol.xmlrpc] is now 'user'
* Added new command `user list`
* Added --user option for all commands
* `manage setup` will use credentials found in configuration file
  for initial setup, preventing asking password.
* Command line options --protocol and --roles reworked
* Configuration file contains all new options

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16
16
#
17
17
 
18
 
from __future__ import print_function
19
18
 
20
19
import getpass
21
20
import hashlib
107
106
]
108
107
 
109
108
_USERS = [
110
 
    # user_id, username, password, protocol
111
 
    (1, 'admin', None, 'xmlrpc'),
 
109
    # user_id, username, protocol, password
 
110
    (1, 'admin', 'xmlrpc', None),
112
111
]
113
112
 
114
113
_PERMISSIONS = [
155
154
"""
156
155
 
157
156
 
158
 
class FabricCredential(_persistence.Persistable):
 
157
class User(_persistence.Persistable):
159
158
    """Class defining a user connecting to a Fabric instance
160
159
    """
161
160
 
163
162
        self._username = username
164
163
        self._protocol = protocol
165
164
        self._password_hash = password_hash
166
 
        self._permissions = FabricCredential.fetch_permissions(username,
167
 
                                                               protocol)
 
165
        self._permissions = User.fetch_permissions(username, protocol)
168
166
 
169
167
    @property
170
168
    def username(self):
279
277
    @staticmethod
280
278
    def delete_user(user_id=None, username=None, protocol=None, persister=None):
281
279
        """Delete a Fabric user"""
282
 
        if bool(user_id) != bool(username):
283
 
            raise AttributeError("Either user_id or username can be given, "
284
 
                                 "not both")
 
280
        if (user_id and username) or (not user_id and not username):
 
281
            raise AttributeError("Use user_id or username, not both")
285
282
 
286
283
        if username and not protocol:
287
284
            raise AttributeError(
315
312
        for statement in _SQL_CONSTRAINTS:
316
313
            persister.exec_stmt(statement)
317
314
 
318
 
        for user_id, username, password, protocol in _USERS:
319
 
            FabricCredential.add_user(username, password, protocol,
320
 
                                      user_id=user_id,
321
 
                                      persister=persister,
322
 
                                      config=config)
 
315
        for user_id, username, protocol, password in _USERS:
 
316
            User.add_user(username, password, protocol,
 
317
                          user_id=user_id,
 
318
                          persister=persister,
 
319
                          config=config)
323
320
 
324
321
        for permission_id, subsystem, component, function, description in \
325
322
                _PERMISSIONS:
326
 
            FabricCredential.add_permission(
 
323
            User.add_permission(
327
324
                subsystem, component, function, description,
328
325
                permission_id=permission_id, persister=persister)
329
326
 
330
327
        for role_id, name, description in _ROLES:
331
 
            FabricCredential.add_role(name, description, role_id=role_id,
332
 
                                      persister=persister)
 
328
            User.add_role(name, description, role_id=role_id,
 
329
                          persister=persister)
333
330
 
334
331
        for role_id, permissions in _ROLE_PERMISSIONS.items():
335
332
            for permission_id in permissions:
336
 
                FabricCredential.add_role_permission(
 
333
                User.add_role_permission(
337
334
                    role_id, permission_id, persister=persister)
338
335
 
339
336
        for user_id, role_id in _USER_ROLES:
340
 
            FabricCredential.add_user_role(user_id, role_id,
341
 
                                           persister=persister)
 
337
            User.add_user_role(user_id, role_id,
 
338
                               persister=persister)
342
339
 
343
340
    @staticmethod
344
341
    def drop(persister=None):
381
378
        cur = persister.exec_stmt(sql, options)
382
379
        row = cur.fetchone()
383
380
        if row:
384
 
            return FabricCredential(username=row.username,
385
 
                                    protocol=row.protocol,
386
 
                                    password_hash=row.password)
 
381
            return User(username=row.username,
 
382
                        protocol=row.protocol,
 
383
                        password_hash=row.password)
387
384
 
388
385
        return None
389
386
 
444
441
            user=username, realm=FABRIC_REALM_XMLRPC,
445
442
            secret=password)).hexdigest()
446
443
 
447
 
    elif protocol == 'console':
448
 
        return hashlib.sha256(username + password).hexdigest()
449
 
 
450
444
    raise _errors.CredentialError(
451
445
        "Password hasing for protocol '{0}' is not implemented.".format(
452
446
            protocol
499
493
    return password
500
494
 
501
495
 
502
 
def check_initial_setup(config, persister):
 
496
def check_initial_setup(config, persister, check_only=False):
503
497
    """Check if admin user has password and if not sets it
504
498
 
505
499
    :param persister: A valid handle to the state store.
506
500
    """
 
501
 
 
502
    # Fetch which protocols have no passwords set for user 'admin'
507
503
    protocols = []
508
504
    for key in FABRIC_PROTOCOL_DEFAULTS.keys():
509
505
        if key.startswith('protocol.'):
513
509
                if not user.password:
514
510
                    protocols.append(tmp)
515
511
 
 
512
    # Try setting password for 'admin' user from configuration file
 
513
    for protocol in protocols:
 
514
        section = 'protocol.' + protocol
 
515
        try:
 
516
            username = config.get(section, 'user')
 
517
        except _config.NoOptionError:
 
518
            username = 'admin'
 
519
 
 
520
        # If there is no password, we have to ask for one.
 
521
        try:
 
522
            password = config.get(section, 'password')
 
523
        except _config.NoOptionError:
 
524
            # No password, so we have to ask for one
 
525
            break
 
526
 
 
527
        persister.begin()
 
528
        try:
 
529
            if username != 'admin':
 
530
                _LOGGER.info("Adding user %s/%s", username, protocol)
 
531
                user_id = User.add_user(username, password, protocol,
 
532
                                        persister=persister)
 
533
            else:
 
534
                _LOGGER.info("Initial password for %s/%s set", username,
 
535
                             protocol)
 
536
                _change_password(username, password, protocol, config,
 
537
                                 persister)
 
538
        except _errors.CredentialError as error:
 
539
            print "Setting password cancelled."
 
540
            _LOGGER.debug(str(error))
 
541
            print error
 
542
            persister.rollback()
 
543
        else:
 
544
            persister.commit()
 
545
            msg = (
 
546
                "Password set for {user}/{protocol} from configuration file."
 
547
            ).format(user=username, protocol=protocol)
 
548
            print msg
 
549
            _LOGGER.info(msg)
 
550
 
 
551
        if username != 'admin':
 
552
            print "Note: {user}/{protocol} has no roles set!".format(
 
553
                user=username, protocol=protocol
 
554
            )
 
555
        else:
 
556
            # No need to ask for password later for this protocol
 
557
            protocols.remove(protocol)
 
558
 
516
559
    if not protocols:
517
560
        # Passwords are set
518
561
        return
519
562
 
520
 
    print("Finishing initial setup")
521
 
    print("=======================")
522
 
    print("Password for admin user is not yet set.")
 
563
    if check_only and protocols:
 
564
        print (
 
565
            "\nPassword for admin user is empty. Please run\n\n"
 
566
            "  shell> mysqlfabric user password admin\n\n"
 
567
            "Make sure the password is empty or commented in section\n"
 
568
            "[protocol.xmlrpc] of the configuration file before executing the\n"
 
569
            "above command."
 
570
        )
 
571
        raise _errors.CredentialError("Check empty admin password failed")
 
572
 
 
573
    print "Finishing initial setup"
 
574
    print "======================="
 
575
    print "Password for admin user is not yet set."
523
576
    password = None
524
577
 
525
578
    while True:
526
 
        password = _get_password("Password for {user}: ".format(user='admin'))
 
579
        password = _get_password("Password for {user}/{protocol}: ".format(
 
580
            user='admin', protocol='xmlrpc'))
527
581
        if password:
528
582
            break
529
583
 
530
584
    if password:
531
 
        options = {
532
 
            "raw": False,
533
 
            "fetch": False,
534
 
            "params": [],
535
 
            "columns": True,
536
 
        }
537
 
        update = ("UPDATE users SET password = %s WHERE username = %s "
538
 
                  "AND protocol = %s")
539
 
 
540
585
        for protocol in protocols:
541
 
            hashed = _hash_password('admin', password, protocol, config)
542
 
            options['params'] = (hashed, 'admin', protocol)
543
 
            persister.exec_stmt(update, options)
544
 
 
 
586
            try:
 
587
                _change_password('admin', password, protocol, config,
 
588
                                 persister)
 
589
            except _errors.CredentialError as error:
 
590
                print "Setting password cancelled."
 
591
                _LOGGER.debug(str(error))
 
592
                print error
 
593
            else:
 
594
                print "Password set."
545
595
    else:
546
596
        # Making sure password is set and there is an error message
547
597
        raise _errors.CredentialError(
548
 
            "Password for admin can not be empty.")
 
598
            "Password for admin can not be empty. Use `user password` command.")
549
599
 
550
600
    persister.commit()
551
601
 
619
669
    :rtype: bool
620
670
    :raises ValueError: if invalid choice has been given.
621
671
    """
 
672
    if not isinstance(default, str):
 
673
        raise AttributeError("default argument should be a string")
622
674
    if default:
623
675
        default = default[0].lower()
624
676
 
666
718
    }
667
719
 
668
720
    roleperms = (
669
 
        "SELECT r.role_id, r.name, r.description AS role_desc, p.permission_id, "
670
 
        "p.description AS perm_desc, p.subsystem, "
 
721
        "SELECT r.role_id, r.name, r.description AS role_desc, "
 
722
        "p.permission_id, p.description AS perm_desc, p.subsystem, "
671
723
        "p.component, p.function "
672
724
        "FROM roles AS r LEFT JOIN role_permissions USING (role_id) "
673
725
        "LEFT JOIN permissions AS p USING (permission_id) ORDER BY r.role_id"
699
751
    label_desc = "Description and Permissions"
700
752
    header = role_fmt.format(role_id="ID", name="Role Name", desc=label_desc,
701
753
                             sel=sel)
702
 
    print(header)
703
 
    print(role_fmt.format(role_id='-' * max_rowid_len, name='-' * max_name_len,
704
 
                          desc='-' * len(label_desc), sel=sel))
 
754
    print header
 
755
    print role_fmt.format(role_id='-' * max_rowid_len, name='-' * max_name_len,
 
756
                          desc='-' * len(label_desc), sel=sel)
705
757
 
706
758
    fmt_perm = '{0}+ {{perm}}'.format(
707
759
        (2 + max_rowid_len + 2 + max_name_len + 2) * ' ')
712
764
        else:
713
765
            sel = ' '
714
766
        name, role_desc, permissions = roles[role_id]
715
 
        print(role_fmt.format(role_id=role_id, name=name, desc=role_desc,
716
 
                              sel=sel))
 
767
        print role_fmt.format(role_id=role_id, name=name, desc=role_desc,
 
768
                              sel=sel)
717
769
        for perm in permissions:
718
 
            print(fmt_perm.format(perm=perm))
719
 
 
720
 
 
721
 
def _role_selection(message=None, persister=None):
 
770
            print fmt_perm.format(perm=perm)
 
771
 
 
772
 
 
773
def _role_selection(message=None, choices=None, persister=None):
722
774
    """Offers user to select roles on the console
723
775
 
724
776
    :param persister: A valid handle to the state store.
725
777
    :param message: Message shown just before prompt.
 
778
    :param choices: Do not ask, just process choices (string or sequence).
726
779
    :return: List of role IDs or role names.
727
780
    :rtype: list
728
781
    :raises errors.CredentialError: if invalid role was given
730
783
    if not persister:
731
784
        persister = _persistence.current_persister()
732
785
 
733
 
    if not message:
734
 
        message = "\nEnter comma separated list of role IDs or names: "
735
 
 
736
 
    choice = raw_input(message)
737
 
    if not choice.strip():
738
 
        return []
739
 
 
740
 
    choices = choice.split(',')
 
786
    if not choices:
 
787
        if not message:
 
788
            message = "\nEnter comma separated list of role IDs or names: "
 
789
 
 
790
        choices = raw_input(message)
 
791
        if not choices.strip():
 
792
            return []
 
793
 
 
794
    if isinstance(choices, str):
 
795
        choices = choices.split(',')
741
796
 
742
797
    options = {
743
798
        "raw": False,
757
812
        if not all(rid.strip() in valid_role_ids for rid in choices):
758
813
            raise ValueError
759
814
    except ValueError:
760
 
        raise _errors.CredentialError("Invalid role in choice.")
 
815
        raise _errors.CredentialError("Found invalid role.")
761
816
 
762
817
    # Only return role IDs
763
818
    result = []
770
825
 
771
826
    return result
772
827
 
 
828
 
 
829
def _change_password(username, password, protocol, config, persister):
 
830
    """Change password of a Fabric user
 
831
 
 
832
    :param username: Username of Fabric user.
 
833
    :param password: Password to which we change.
 
834
    :param protocol: Protocol for this user.
 
835
    :param config: Fabric configuration.
 
836
    :param persister: A valid handle to the state store.
 
837
 
 
838
    :raise _errors.CredentialError: if any error occurs while updating data
 
839
    """
 
840
    try:
 
841
        persister.begin()
 
842
        options = {
 
843
            "raw": False,
 
844
            "fetch": False,
 
845
            "params": (),
 
846
            "columns": True,
 
847
        }
 
848
        update = ("UPDATE users SET password = %s WHERE username = %s"
 
849
                  " AND protocol = %s")
 
850
        hashed = _hash_password(username, password, protocol,
 
851
                                config)
 
852
        options['params'] = (hashed, username, protocol)
 
853
        persister.exec_stmt(update, options)
 
854
    except Exception as error:
 
855
        # We rollback and re-raise
 
856
        persister.rollback()
 
857
        raise _errors.CredentialError("Error updating password: {0}".format(
 
858
            str(error)
 
859
        ))
 
860
    persister.commit()
 
861
 
 
862
 
773
863
def validate_username(username, allow_empty=False):
774
864
    """Validates a username
775
865
 
815
905
 
816
906
 
817
907
class UserCommand(_command.Command):
818
 
 
819
908
    """Base class for all user commands"""
820
909
 
821
910
    group_name = 'user'
847
936
        if not title:
848
937
            title = self.description
849
938
 
850
 
        print(title)
851
 
        print("=" * len(title))
 
939
        print title
 
940
        print "=" * len(title)
852
941
 
853
942
        if username:
854
 
            print("Username: {0}".format(username))
 
943
            print "Username: {0}".format(username)
855
944
        else:
856
945
            username = validate_username(raw_input("Enter username: "))
857
946
 
858
947
        if self.options.protocol:
859
948
            protocol = self.options.protocol
860
 
            print("Protocol: {0}".format(protocol))
 
949
            print "Protocol: {0}".format(protocol)
861
950
        else:
862
951
            prompt = "Protocol (default {0}): ".format(FABRIC_DEFAULT_PROTOCOL)
863
952
            protocol = validate_protocol(raw_input(prompt),
864
 
                                              allow_empty=True)
 
953
                                         allow_empty=True)
865
954
            if not protocol:
866
955
                protocol = FABRIC_DEFAULT_PROTOCOL
867
956
 
875
964
 
876
965
class UserAdd(UserCommand):
877
966
 
878
 
    """Add a new Fabric user"""
 
967
    """Add a new Fabric user.
 
968
 
 
969
    * protocol: Protocol of the user (for example 'xmlrpc')
 
970
    * roles: Comma separated list of roles, IDs or names (see `role list`)
 
971
 
 
972
    """
879
973
 
880
974
    command_name = 'add'
881
975
    description = 'Add a new Fabric user'
882
976
 
883
 
    def execute(self, username, protocol=None, role=None):
 
977
    def execute(self, username, protocol=None, roles=None):
884
978
        """Add a new Fabric user"""
885
979
        username, password, protocol = self._ask_credentials(
886
980
            username, ask_password=False)
891
985
                "User {user}/{protocol} already exists".format(
892
986
                    user=username, protocol=protocol))
893
987
 
894
 
        password = _get_password('Password:')
 
988
        password = _get_password('Password: ')
895
989
 
896
990
        if not password:
897
991
            raise _errors.CredentialError("Can not set empty password")
898
992
 
899
 
        try:
900
 
            self.persister.begin()
901
 
            user_id = FabricCredential.add_user(username, password, protocol)
902
 
            print("\nSelect role(s) for new user")
 
993
        role_list = []
 
994
        if roles:
 
995
            role_list = [role.strip() for role in roles.split(',')]
 
996
        else:
 
997
            print "\nSelect role(s) for new user"
903
998
            _role_listing()
904
 
            roles = _role_selection(persister=self.persister)
905
 
            if not roles:
906
 
                print("You can always assign roles later using the "
907
 
                      "'user addrole' command")
 
999
        role_list = _role_selection(persister=self.persister, choices=role_list)
 
1000
 
 
1001
        try:
 
1002
            self.persister.begin()
 
1003
            user_id = User.add_user(username, password, protocol)
 
1004
            if not role_list:
 
1005
                print ("You can always assign roles later using the "
 
1006
                       "'user addrole' command")
908
1007
            else:
909
 
                for role_id in roles:
910
 
                    FabricCredential.add_user_role(user_id, int(role_id))
 
1008
                for role_id in role_list:
 
1009
                    User.add_user_role(user_id, int(role_id))
911
1010
        except Exception:
912
1011
            # Whatever happens, we rollback and re-raise
913
1012
            self.persister.rollback()
914
 
            print("Adding user cancelled.")
 
1013
            print "Adding user cancelled."
915
1014
            raise
916
1015
 
917
1016
        self.persister.commit()
918
 
        print("Fabric user added.")
 
1017
        print "Fabric user added."
919
1018
 
920
1019
 
921
1020
class UserDelete(UserCommand):
922
1021
 
923
 
    """Delete a Fabric user"""
 
1022
    """Delete a Fabric user.
 
1023
 
 
1024
    * protocol: Protocol of the user (for example 'xmlrpc')
 
1025
    * force: Do not ask for confirmation
 
1026
 
 
1027
    """
924
1028
 
925
1029
    command_name = 'delete'
926
1030
    description = 'Delete a Fabric user'
946
1050
 
947
1051
        try:
948
1052
            self.persister.begin()
949
 
            FabricCredential.delete_user(username=username, protocol=protocol)
 
1053
            User.delete_user(username=username, protocol=protocol)
950
1054
        except Exception:
951
1055
            # We rollback and re-raise
952
1056
            self.persister.rollback()
953
 
            print("Removing user cancelled.")
 
1057
            print "Removing user cancelled."
954
1058
            raise
955
1059
 
956
1060
        self.persister.commit()
957
 
        print("Fabric user deleted.")
 
1061
        print "Fabric user deleted."
958
1062
 
959
1063
 
960
1064
class UserPassword(UserCommand):
961
1065
 
962
 
    """Change password of a Fabric user"""
 
1066
    """Change password of a Fabric user.
 
1067
 
 
1068
    * protocol: Protocol of the user (for example 'xmlrpc')
 
1069
 
 
1070
    """
963
1071
 
964
1072
    command_name = 'password'
965
1073
    description = 'Change password a Fabric user'
976
1084
                "No user {user}/{protocol}".format(
977
1085
                    user=username, protocol=protocol))
978
1086
 
979
 
        password = _get_password('New password:')
 
1087
        password = _get_password('New password: ')
980
1088
 
981
1089
        if password:
982
1090
            try:
983
 
                self.persister.begin()
984
 
                options = {
985
 
                    "raw": False,
986
 
                    "fetch": False,
987
 
                    "params": (),
988
 
                    "columns": True,
989
 
                }
990
 
                update = ("UPDATE users SET password = %s WHERE username = %s"
991
 
                          " AND protocol = %s")
992
 
                hashed = _hash_password(username, password, protocol,
993
 
                                        self.config)
994
 
                options['params'] = (hashed, username, protocol)
995
 
                self.persister.exec_stmt(update, options)
996
 
            except Exception:
997
 
                # We rollback and re-raise
998
 
                print("Changing password cancelled.")
999
 
                raise
1000
 
            self.persister.commit()
1001
 
            print("Password changed.")
 
1091
                _change_password(username, password, protocol, self.config,
 
1092
                                 self.persister)
 
1093
            except _errors.CredentialError as error:
 
1094
                print "Changing password cancelled."
 
1095
                _LOGGER.debug(str(error))
 
1096
                print error
 
1097
            else:
 
1098
                print "Password changed."
1002
1099
        else:
1003
1100
            raise _errors.CredentialError("Can not set empty password")
1004
1101
 
1005
1102
 
1006
1103
class UserRoles(UserCommand):
1007
1104
 
1008
 
    """Change roles for a Fabric user"""
 
1105
    """Change roles for a Fabric user
 
1106
 
 
1107
    * protocol: Protocol of the user (for example 'xmlrpc')
 
1108
    * roles: Comma separated list of roles, IDs or names (see `role list`)
 
1109
 
 
1110
    """
1009
1111
 
1010
1112
    command_name = 'roles'
1011
1113
    description = 'Change roles for a Fabric user'
1012
1114
 
1013
 
    def execute(self, username, protocol=None):
 
1115
    def execute(self, username, protocol=None, roles=None):
1014
1116
        """Change roles for a Fabric user
1015
1117
        """
1016
1118
        username, password, protocol = self._ask_credentials(
1017
 
            username, ask_password=True)
 
1119
            username, ask_password=False)
1018
1120
 
1019
1121
        # Check if user exists
1020
1122
        user = get_user(username, protocol)
1023
1125
                "No user {user}/{protocol}".format(user=username,
1024
1126
                                                   protocol=protocol))
1025
1127
 
1026
 
        self.persister.begin()
1027
 
        options = {
1028
 
            "raw": False,
1029
 
            "fetch": False,
1030
 
            "params": (user.user_id,),
1031
 
            "columns": True,
1032
 
        }
1033
 
        cur = self.persister.exec_stmt(
1034
 
            "SELECT role_id FROM user_roles WHERE user_id = %s",
1035
 
            options)
1036
 
        current_roles = [row.role_id for row in cur]
1037
 
 
1038
 
        try:
1039
 
            self.persister.begin()
1040
 
            print("\nSelect new role(s) for user, replacing current roles.")
1041
 
            print("Current roles are marke with an X.")
 
1128
        exit_text_removed = (
 
1129
            "Roles for {user}/{protocol} removed."
 
1130
        ).format(user=username, protocol=protocol)
 
1131
        exit_text_updated = (
 
1132
            "Roles for {user}/{protocol} updated."
 
1133
        ).format(user=username, protocol=protocol)
 
1134
 
 
1135
        confirmed = False
 
1136
        role_list = []
 
1137
        if not roles:
 
1138
            options = {
 
1139
                "raw": False,
 
1140
                "fetch": False,
 
1141
                "params": (user.user_id,),
 
1142
                "columns": True,
 
1143
            }
 
1144
            cur = self.persister.exec_stmt(
 
1145
                "SELECT role_id FROM user_roles WHERE user_id = %s",
 
1146
                options)
 
1147
            current_roles = [row.role_id for row in cur]
 
1148
 
 
1149
            print "\nSelect new role(s) for user, replacing current roles."
 
1150
            print "Current roles are marke with an X."
1042
1151
            _role_listing(selected=current_roles)
1043
 
            roles = _role_selection(persister=self.persister)
 
1152
            role_list = _role_selection(persister=self.persister)
1044
1153
 
1045
 
            if not roles:
1046
 
                confirm_text = "Remove all roles of user {username}?"
1047
 
                exit_text = "Fabric user roles removed."
 
1154
            if not role_list:
 
1155
                confirm_text = (
 
1156
                    "Remove all roles of user {user}/{protocol}?"
 
1157
                ).format(user=username, protocol=protocol)
 
1158
                exit_text = exit_text_removed
1048
1159
                default = 'n'
1049
1160
            else:
1050
 
                confirm_text = "Replace roles of user {username}?"
1051
 
                exit_text = "Fabric user roles updated."
 
1161
                confirm_text = (
 
1162
                    "Replace roles of user {user}/{protocol}?"
 
1163
                ).format(user=username, protocol=protocol)
 
1164
                exit_text = exit_text_updated
1052
1165
                default = 'y'
1053
1166
 
1054
 
            if confirm(confirm_text.format(username=user.username),
1055
 
                       default=default):
 
1167
            confirmed = confirm(confirm_text.format(username=user.username),
 
1168
                                default=default)
 
1169
        else:
 
1170
            # From command line option --roles
 
1171
            confirmed = True
 
1172
            if roles.strip() == "0":
 
1173
                exit_text = exit_text_removed
 
1174
                role_list = []
 
1175
            else:
 
1176
                exit_text = exit_text_updated
 
1177
                role_list = [role.strip() for role in roles.split(',')]
 
1178
 
 
1179
                role_list = _role_selection(persister=self.persister,
 
1180
                                            choices=role_list)
 
1181
 
 
1182
        if confirmed:
 
1183
            try:
 
1184
                self.persister.begin()
 
1185
                options = {
 
1186
                    "raw": False,
 
1187
                    "fetch": False,
 
1188
                    "params": (user.user_id,),
 
1189
                    "columns": True,
 
1190
                }
1056
1191
                self.persister.exec_stmt(
1057
1192
                    "DELETE FROM user_roles WHERE user_id = %s", options)
1058
 
                for role_id in roles:
1059
 
                    FabricCredential.add_user_role(user.user_id, int(role_id))
 
1193
                for role_id in role_list:
 
1194
                    User.add_user_role(user.user_id, int(role_id))
 
1195
            except Exception:
 
1196
                # Whatever happens, we rollback and re-raise
 
1197
                self.persister.rollback()
 
1198
                print "Changing roles for user cancelled."
 
1199
                raise
1060
1200
            else:
1061
 
                exit_text = "Changing roles for user cancelled."
1062
 
 
1063
 
        except Exception:
1064
 
            # Whatever happens, we rollback and re-raise
1065
 
            self.persister.rollback()
1066
 
            print("Changing roles for user cancelled.")
1067
 
            raise
 
1201
                self.persister.commit()
 
1202
                print exit_text
1068
1203
        else:
1069
 
            self.persister.commit()
1070
 
            print(exit_text)
 
1204
            print "Changing roles cancelled."
 
1205
 
 
1206
 
 
1207
class UserList(UserCommand):
 
1208
    """List users and their roles
 
1209
    """
 
1210
 
 
1211
    group_name = 'user'
 
1212
    command_name = 'list'
 
1213
    description = 'List roles and their permissions'
 
1214
 
 
1215
    def execute(self):
 
1216
        """Display list of users
 
1217
        """
 
1218
        persister = _persistence.current_persister()
 
1219
 
 
1220
        options = {
 
1221
            "raw": False,
 
1222
            "fetch": False,
 
1223
            "columns": True,
 
1224
        }
 
1225
 
 
1226
        role_perms = (
 
1227
            "SELECT u.username, u.protocol, r.name as role_name, "
 
1228
            "r.description AS role_desc,"
 
1229
            "p.permission_id, p.description AS perm_desc, p.subsystem, "
 
1230
            "p.component, p.function "
 
1231
            "FROM users as u LEFT JOIN user_roles AS ur USING (user_id) "
 
1232
            "LEFT JOIN roles AS r USING (role_id) "
 
1233
            "LEFT JOIN role_permissions USING (role_id) "
 
1234
            "LEFT JOIN permissions AS p USING (permission_id) "
 
1235
            "ORDER BY u.username, u.protocol"
 
1236
        )
 
1237
        cur = persister.exec_stmt(role_perms, options)
 
1238
        roles = {}
 
1239
        max_username_len = 0
 
1240
        max_protocol_len = 0
 
1241
 
 
1242
        user_info = {}
 
1243
        for row in cur:
 
1244
            if len(row.username) > max_username_len:
 
1245
                max_username_len = len(row.username)
 
1246
            if len(row.protocol) > max_protocol_len:
 
1247
                max_protocol_len = len(row.protocol)
 
1248
 
 
1249
            user_tuple = (row.username, row.protocol)
 
1250
            if user_tuple not in user_info:
 
1251
                user_info[user_tuple] = []
 
1252
            if row.role_name and row.role_name not in user_info[user_tuple]:
 
1253
                user_info[user_tuple].append(row.role_name)
 
1254
 
 
1255
        # Minimum sizes
 
1256
        max_username_len = max(9, max_username_len)
 
1257
        max_protocol_len = max(12, max_protocol_len)
 
1258
 
 
1259
        role_fmt = ("{{username:{userlen}}} {{protocol:{protlen}}} "
 
1260
                    "{{roles}}".format(userlen=max_username_len,
 
1261
                                       protlen=max_protocol_len))
 
1262
 
 
1263
        header = role_fmt.format(username="Username", protocol="Protocol",
 
1264
                                 roles="Roles")
 
1265
        print header
 
1266
        print role_fmt.format(username='-' * max_username_len,
 
1267
                              protocol='-' * max_protocol_len,
 
1268
                              roles='-' * 20)
 
1269
 
 
1270
        for user_tuple, roles in user_info.iteritems():
 
1271
            username, protocol = user_tuple
 
1272
            if roles:
 
1273
                role_list = ', '.join(roles)
 
1274
            else:
 
1275
                role_list = '(no roles set)'
 
1276
            print role_fmt.format(username=username,
 
1277
                                  protocol=protocol,
 
1278
                                  roles=role_list)
1071
1279
 
1072
1280
 
1073
1281
class RoleList(UserCommand):
1074
 
 
1075
 
    """List roles and their permissions"""
 
1282
    """List roles and associated permissions
 
1283
    """
1076
1284
 
1077
1285
    group_name = 'role'
1078
1286
    command_name = 'list'
1082
1290
        _role_listing(persister=self.persister)
1083
1291
 
1084
1292
 
1085
 
def check_credentials(group, command, config=None,
 
1293
def check_credentials(group, command, config,
1086
1294
                      username=None, password=None, protocol=None):
1087
1295
    """Check credentials using configuration
1088
1296
 
1097
1305
 
1098
1306
    section = 'protocol.' + protocol
1099
1307
 
1100
 
    if config:
1101
 
        username = config.get(section, 'username')
1102
 
        password = config.get(section, 'password')
1103
 
        realm = config.get(section, 'realm', vars=FABRIC_PROTOCOL_DEFAULTS)
 
1308
    username = config.get(section, 'user')
 
1309
    password = config.get(section, 'password')
 
1310
    realm = config.get(section, 'realm', vars=FABRIC_PROTOCOL_DEFAULTS)
1104
1311
 
1105
 
    user = FabricCredential.fetch_user(username, protocol, config,
1106
 
                                       password=password, realm=realm)
 
1312
    user = User.fetch_user(username, protocol, config,
 
1313
                           password=password, realm=realm)
1107
1314
 
1108
1315
    if not user:
1109
 
        import traceback
1110
 
        traceback.print_exc()
 
1316
        _LOGGER.info("Failed login for user %s/%s", username, protocol)
1111
1317
        raise _errors.CredentialError("Login failed")
1112
1318
    if not user.has_permission('core', group, command):
 
1319
        _LOGGER.info("Permission denied for user %s/%s", username, protocol)
1113
1320
        raise _errors.CredentialError("No permission")
1114