Browse Source

add command for initializedb and users

Guénaël Muller 7 years ago
parent
commit
a1208cc2ab

+ 8 - 1
setup.py View File

@@ -25,6 +25,7 @@ requires = [
25 25
     'alembic',
26 26
     'hapic',
27 27
     'marshmallow',
28
+    'cliff',
28 29
 ]
29 30
 
30 31
 tests_require = [
@@ -75,7 +76,13 @@ setup(
75 76
             'main = tracim:main',
76 77
         ],
77 78
         'console_scripts': [
78
-            'initialize_tracim_db = tracim.scripts.initializedb:main',
79
+            'tracimcli = tracim.command:main',
79 80
         ],
81
+        'tracimcli': [
82
+            'test = tracim.command:TestTracimCommand',
83
+            'user_create = tracim.command.user:CreateUserCommand',
84
+            'user_update = tracim.command.user:UpdateUserCommand',
85
+            'db_init = tracim.command.initializedb:InitializeDBCommand',
86
+        ]
80 87
     },
81 88
 )

+ 100 - 0
tracim/command/__init__.py View File

@@ -0,0 +1,100 @@
1
+# -*- coding: utf-8 -*-
2
+import sys
3
+import argparse
4
+import transaction
5
+
6
+from cliff.app import App
7
+from cliff.command import Command
8
+from cliff.commandmanager import CommandManager
9
+
10
+from pyramid.paster import bootstrap
11
+from tracim.exceptions import CommandAbortedError
12
+
13
+
14
+
15
+class TracimCLI(App):
16
+
17
+    def __init__(self):
18
+        super(TracimCLI, self).__init__(
19
+            description='TracimCli',
20
+            version='0.1',
21
+            command_manager=CommandManager('tracimcli'),
22
+            deferred_help=True,
23
+            )
24
+
25
+    def initialize_app(self, argv):
26
+        self.LOG.debug('initialize_app')
27
+
28
+    def prepare_to_run_command(self, cmd):
29
+        self.LOG.debug('prepare_to_run_command %s', cmd.__class__.__name__)
30
+
31
+    def clean_up(self, cmd, result, err):
32
+        self.LOG.debug('clean_up %s', cmd.__class__.__name__)
33
+        if err:
34
+            self.LOG.debug('got an error: %s', err)
35
+
36
+
37
+def main(argv=sys.argv[1:]):
38
+    myapp = TracimCLI()
39
+    return myapp.run(argv)
40
+
41
+
42
+if __name__ == "__main__":
43
+    main()
44
+
45
+
46
+class AppContextCommand(Command):
47
+    """
48
+    Command who initialize app context at beginning of take_action method.
49
+    """
50
+    auto_setup_context = True
51
+
52
+    def take_action(self, parsed_args):
53
+        super(AppContextCommand, self).take_action(parsed_args)
54
+        if self.auto_setup_context:
55
+            with bootstrap(parsed_args.config_file) as app_context:
56
+                with app_context['request'].tm:
57
+                    self.take_app_action(parsed_args, app_context)
58
+
59
+    def get_parser(self, prog_name):
60
+        parser = super(AppContextCommand, self).get_parser(prog_name)
61
+
62
+        parser.add_argument(
63
+            "-c",
64
+            "--config",
65
+            help='application config file to read (default: development.ini)',
66
+            dest='config_file',
67
+            default="development.ini"
68
+        )
69
+        return parser
70
+
71
+    # def run(self, parsed_args):
72
+    #     super().run(parsed_args)
73
+    #     transaction.commit()
74
+
75
+
76
+class TestTracimCommand(AppContextCommand):
77
+
78
+    def take_app_action(self, parser, app_context):
79
+        print('test')
80
+
81
+
82
+class Extender(argparse.Action):
83
+    """
84
+    Copied class from http://stackoverflow.com/a/12461237/801924
85
+    """
86
+    def __call__(self, parser, namespace, values, option_strings=None):
87
+        # Need None here incase `argparse.SUPPRESS` was supplied for `dest`
88
+        dest = getattr(namespace, self.dest, None)
89
+        # print dest,self.default,values,option_strings
90
+        if not hasattr(dest, 'extend') or dest == self.default:
91
+            dest = []
92
+            setattr(namespace, self.dest, dest)
93
+            # if default isn't set to None, this method might be called
94
+            # with the default as `values` for other arguements which
95
+            # share this destination.
96
+            parser.set_defaults(**{self.dest: None})
97
+        try:
98
+            dest.extend(values)
99
+        except ValueError:
100
+            dest.append(values)

+ 49 - 0
tracim/command/initializedb.py View File

@@ -0,0 +1,49 @@
1
+# -*- coding: utf-8 -*-
2
+import os
3
+import sys
4
+import transaction
5
+
6
+from pyramid.paster import (
7
+    get_appsettings,
8
+    setup_logging,
9
+    )
10
+from pyramid.scripts.common import parse_vars
11
+
12
+from tracim.command import AppContextCommand
13
+from tracim.models.meta import DeclarativeBase
14
+from tracim.models import (
15
+    get_engine,
16
+    get_session_factory,
17
+    get_tm_session,
18
+    )
19
+
20
+
21
+class InitializeDBCommand(AppContextCommand):
22
+    auto_setup_context = False
23
+
24
+    def get_description(self):
25
+        return "Initialize DB"
26
+
27
+    def get_epilog(self):
28
+        return "################"
29
+
30
+    def get_parser(self, prog_name):
31
+        parser = super().get_parser(prog_name)
32
+        return parser
33
+
34
+    def take_action(self, parsed_args):
35
+        super(InitializeDBCommand, self).take_action(parsed_args)
36
+        config_uri = parsed_args.config_file
37
+
38
+        setup_logging(config_uri)
39
+        settings = get_appsettings(config_uri)
40
+        engine = get_engine(settings)
41
+        DeclarativeBase.metadata.create_all(engine)
42
+        session_factory = get_session_factory(engine)
43
+
44
+        with transaction.manager:
45
+            pass
46
+            # dbsession = get_tm_session(session_factory, transaction.manager)
47
+            # model = MyModel(name='one', value=1)
48
+            # dbsession.add(model)
49
+            # Add global manager data, just for test

+ 210 - 0
tracim/command/user.py View File

@@ -0,0 +1,210 @@
1
+# -*- coding: utf-8 -*-
2
+import transaction
3
+from sqlalchemy.exc import IntegrityError
4
+
5
+from tracim import CFG
6
+from tracim.command import AppContextCommand
7
+from tracim.command import Extender
8
+#from tracim.lib.auth.ldap import LDAPAuth
9
+#from tracim.lib.daemons import DaemonsManager
10
+#from tracim.lib.daemons import RadicaleDaemon
11
+#from tracim.lib.email import get_email_manager
12
+from tracim.exceptions import AlreadyExistError
13
+from tracim.exceptions import CommandAbortedError
14
+from tracim.lib.core.group import GroupApi
15
+from tracim.lib.core.user import UserApi
16
+from tracim.models import User
17
+
18
+
19
+class UserCommand(AppContextCommand):
20
+
21
+    ACTION_CREATE = 'create'
22
+    ACTION_UPDATE = 'update'
23
+
24
+    action = NotImplemented
25
+
26
+    def get_description(self):
27
+        return '''Create or update user.'''
28
+
29
+    def get_parser(self, prog_name):
30
+        parser = super().get_parser(prog_name)
31
+
32
+        parser.add_argument(
33
+            "-l",
34
+            "--login",
35
+            help='User login (email)',
36
+            dest='login',
37
+            required=True
38
+        )
39
+
40
+        parser.add_argument(
41
+            "-p",
42
+            "--password",
43
+            help='User password',
44
+            dest='password',
45
+            required=False,
46
+            default=None
47
+        )
48
+
49
+        parser.add_argument(
50
+            "-g",
51
+            "--add-to-group",
52
+            help='Add user to group',
53
+            dest='add_to_group',
54
+            nargs='*',
55
+            action=Extender,
56
+            default=[],
57
+        )
58
+
59
+        parser.add_argument(
60
+            "-rmg",
61
+            "--remove-from-group",
62
+            help='Remove user from group',
63
+            dest='remove_from_group',
64
+            nargs='*',
65
+            action=Extender,
66
+            default=[],
67
+        )
68
+
69
+        parser.add_argument(
70
+            "--send-email",
71
+            help='Send mail to user',
72
+            dest='send_email',
73
+            required=False,
74
+            action='store_true',
75
+            default=False,
76
+        )
77
+
78
+        return parser
79
+
80
+    def _user_exist(self, login):
81
+        return self._user_api.user_with_email_exists(login)
82
+
83
+    def _get_group(self, name):
84
+        return self._group_api.get_one_with_name(name)
85
+
86
+    def _add_user_to_named_group(self, user, group_name):
87
+        group = self._get_group(group_name)
88
+        if user not in group.users:
89
+            group.users.append(user)
90
+        self._session.flush()
91
+
92
+    def _remove_user_from_named_group(self, user, group_name):
93
+        group = self._get_group(group_name)
94
+        if user in group.users:
95
+            group.users.remove(user)
96
+        self._session.flush()
97
+
98
+    def _create_user(self, login, password, **kwargs):
99
+        if not password:
100
+            if self._password_required():
101
+                raise CommandAbortedError("You must provide -p/--password parameter")
102
+            password = ''
103
+
104
+        try:
105
+            user =self._user_api.create_user(email=login)
106
+            user.password = password
107
+            self._user_api.save(user)
108
+            # TODO - G.M - 04-04-2018 - [Caldav] Check this code
109
+            # # We need to enable radicale if it not already done
110
+            # daemons = DaemonsManager()
111
+            # daemons.run('radicale', RadicaleDaemon)
112
+
113
+            self._user_api.execute_created_user_actions(user)
114
+        except IntegrityError:
115
+            self._session.rollback()
116
+            raise AlreadyExistError()
117
+
118
+        return user
119
+
120
+    def _update_password_for_login(self, login, password):
121
+        user = self._user_api.get_one_by_email(login)
122
+        user.password = password
123
+        self._session.flush()
124
+        transaction.commit()
125
+
126
+    def take_app_action(self, parsed_args, app_context):
127
+        # TODO - G.M - 05-04-2018 -Refactor this in order
128
+        # to not setup object var outside of __init__ .
129
+        self._session = app_context['request'].dbsession
130
+        self._app_config = app_context['registry'].settings['CFG']
131
+        self._user_api = UserApi(
132
+            current_user=None,
133
+            session=self._session,
134
+            config=self._app_config,
135
+        )
136
+        self._group_api = GroupApi(
137
+            current_user=None,
138
+            session=self._session,
139
+        )
140
+        user = self._proceed_user(parsed_args)
141
+        self._proceed_groups(user, parsed_args)
142
+
143
+        print("User created/updated")
144
+
145
+    def _proceed_user(self, parsed_args):
146
+        self._check_context(parsed_args)
147
+
148
+        if self.action == self.ACTION_CREATE:
149
+            try:
150
+                user = self._create_user(
151
+                    login=parsed_args.login,
152
+                    password=parsed_args.password
153
+                )
154
+            except AlreadyExistError:
155
+                raise CommandAbortedError("Error: User already exist (use `user update` command instead)")
156
+            # TODO - G.M - 04-04-2018 - [Email] Check this code
157
+            # if parsed_args.send_email:
158
+            #     email_manager = get_email_manager()
159
+            #     email_manager.notify_created_account(
160
+            #         user=user,
161
+            #         password=parsed_args.password,
162
+            #     )
163
+
164
+        else:
165
+            if parsed_args.password:
166
+                self._update_password_for_login(
167
+                    login=parsed_args.login,
168
+                    password=parsed_args.password
169
+                )
170
+            user = self._user_api.get_one_by_email(parsed_args.login)
171
+
172
+        return user
173
+
174
+    def _proceed_groups(self, user, parsed_args):
175
+        # User always in "users" group
176
+        self._add_user_to_named_group(user, 'users')
177
+
178
+        for group_name in parsed_args.add_to_group:
179
+            self._add_user_to_named_group(user, group_name)
180
+
181
+        for group_name in parsed_args.remove_from_group:
182
+            self._remove_user_from_named_group(user, group_name)
183
+
184
+    def _password_required(self):
185
+        # TODO - G.M - 04-04-2018 - [LDAP] Check this code
186
+        # if config.get('auth_type') == LDAPAuth.name:
187
+        #     return False
188
+        return True
189
+
190
+    def _check_context(self, parsed_args):
191
+        # TODO - G.M - 04-04-2018 - [LDAP] Check this code
192
+        # if config.get('auth_type') == LDAPAuth.name:
193
+        #     auth_instance = config.get('auth_instance')
194
+        #     if not auth_instance.ldap_auth.user_exist(parsed_args.login):
195
+        #         raise LDAPUserUnknown(
196
+        #             "LDAP is enabled and user with login/email \"%s\" not found in LDAP" % parsed_args.login
197
+        #         )
198
+        pass
199
+
200
+
201
+class CreateUserCommand(UserCommand):
202
+    action = UserCommand.ACTION_CREATE
203
+
204
+
205
+class UpdateUserCommand(UserCommand):
206
+    action = UserCommand.ACTION_UPDATE
207
+
208
+
209
+class LDAPUserUnknown(CommandAbortedError):
210
+    pass

+ 0 - 2
tracim/scripts/__init__.py View File

@@ -1,2 +0,0 @@
1
-# -*- coding: utf-8 -*-
2
-# package

+ 0 - 46
tracim/scripts/initializedb.py View File

@@ -1,46 +0,0 @@
1
-# -*- coding: utf-8 -*-
2
-import os
3
-import sys
4
-import transaction
5
-
6
-from pyramid.paster import (
7
-    get_appsettings,
8
-    setup_logging,
9
-    )
10
-
11
-from pyramid.scripts.common import parse_vars
12
-
13
-from ..models.meta import DeclarativeBase
14
-from ..models import (
15
-    get_engine,
16
-    get_session_factory,
17
-    get_tm_session,
18
-    )
19
-
20
-
21
-def usage(argv):
22
-    cmd = os.path.basename(argv[0])
23
-    print('usage: %s <config_uri> [var=value]\n'
24
-          '(example: "%s development.ini")' % (cmd, cmd))
25
-    sys.exit(1)
26
-
27
-
28
-def main(argv=sys.argv):
29
-    if len(argv) < 2:
30
-        usage(argv)
31
-    config_uri = argv[1]
32
-    options = parse_vars(argv[2:])
33
-    setup_logging(config_uri)
34
-    settings = get_appsettings(config_uri, options=options)
35
-
36
-    engine = get_engine(settings)
37
-    DeclarativeBase.metadata.create_all(engine)
38
-
39
-    session_factory = get_session_factory(engine)
40
-    # TODO - G.M - 28-03-2018 - [Cleanup] Remove code related to example
41
-    with transaction.manager:
42
-        pass
43
-        # dbsession = get_tm_session(session_factory, transaction.manager)
44
-        # model = MyModel(name='one', value=1)
45
-        # dbsession.add(model)
46
-        # Add global manager data, just for test