##// END OF EJS Templates
Improve API with user/group/repo CRUD methods
Nicolas VINOT -
r1584:3338a099 beta
parent child Browse files
Show More
@@ -1,6 +1,8 b''
1 syntax: glob
1 syntax: glob
2 *.pyc
2 *.pyc
3 *.swp
3 *.swp
4 *.ini
5 Paste*
4
6
5 syntax: regexp
7 syntax: regexp
6 ^build
8 ^build
@@ -14,3 +16,4 b' syntax: regexp'
14 ^test\.db$
16 ^test\.db$
15 ^repositories\.config$
17 ^repositories\.config$
16 ^RhodeCode\.egg-info$
18 ^RhodeCode\.egg-info$
19 ^env$
@@ -2,10 +2,11 b' import traceback'
2 import logging
2 import logging
3
3
4 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
4 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
5 from rhodecode.lib.auth import HasPermissionAllDecorator
5 from rhodecode.lib.auth import HasPermissionAllDecorator, HasPermissionAnyDecorator
6 from rhodecode.model.scm import ScmModel
6 from rhodecode.model.scm import ScmModel
7
7
8 from rhodecode.model.db import User, UsersGroup, Repository
8 from rhodecode.model.db import User, UsersGroup, UsersGroupMember, Group, Repository
9 from rhodecode.model.repo import RepoModel
9
10
10 log = logging.getLogger(__name__)
11 log = logging.getLogger(__name__)
11
12
@@ -13,25 +14,25 b' log = logging.getLogger(__name__)'
13 class ApiController(JSONRPCController):
14 class ApiController(JSONRPCController):
14 """
15 """
15 API Controller
16 API Controller
16
17
17
18
18 Each method needs to have USER as argument this is then based on given
19 Each method needs to have USER as argument this is then based on given
19 API_KEY propagated as instance of user object
20 API_KEY propagated as instance of user object
20
21
21 Preferably this should be first argument also
22 Preferably this should be first argument also
22
23
23
24
24 Each function should also **raise** JSONRPCError for any
25 Each function should also **raise** JSONRPCError for any
25 errors that happens
26 errors that happens
26
27
27 """
28 """
28
29
29 @HasPermissionAllDecorator('hg.admin')
30 @HasPermissionAllDecorator('hg.admin')
30 def pull(self, apiuser, repo):
31 def pull(self, apiuser, repo):
31 """
32 """
32 Dispatch pull action on given repo
33 Dispatch pull action on given repo
33
34
34
35
35 :param user:
36 :param user:
36 :param repo:
37 :param repo:
37 """
38 """
@@ -47,28 +48,30 b' class ApiController(JSONRPCController):'
47
48
48
49
49 @HasPermissionAllDecorator('hg.admin')
50 @HasPermissionAllDecorator('hg.admin')
50 def create_user(self, apiuser, username, password, active, admin, name,
51 def create_user(self, apiuser, username, password, name,
51 lastname, email):
52 lastname, email, active=True, admin=False, ldap_dn=None):
52 """
53 """
53 Creates new user
54 Creates new user
54
55
55 :param apiuser:
56 :param apiuser:
56 :param username:
57 :param username:
57 :param password:
58 :param password:
58 :param active:
59 :param admin:
60 :param name:
59 :param name:
61 :param lastname:
60 :param lastname:
62 :param email:
61 :param email:
62 :param active:
63 :param admin:
64 :param ldap_dn:
63 """
65 """
64
66
65 form_data = dict(username=username,
67 form_data = dict(username=username,
66 password=password,
68 password=password,
67 active=active,
69 active=active,
68 admin=admin,
70 admin=admin,
69 name=name,
71 name=name,
70 lastname=lastname,
72 lastname=lastname,
71 email=email)
73 email=email,
74 ldap_dn=ldap_dn)
72 try:
75 try:
73 u = User.create(form_data)
76 u = User.create(form_data)
74 return {'id':u.user_id,
77 return {'id':u.user_id,
@@ -77,12 +80,27 b' class ApiController(JSONRPCController):'
77 log.error(traceback.format_exc())
80 log.error(traceback.format_exc())
78 raise JSONRPCError('failed to create user %s' % name)
81 raise JSONRPCError('failed to create user %s' % name)
79
82
83 @HasPermissionAllDecorator('hg.admin')
84 def list_users(self, apiuser):
85 """"
86 Lists all users
87
88 :param apiuser
89 """
90 result = []
91 for user in User.getAll():
92 result.append({ 'username':user.username,
93 'name': user.name,
94 'lastname': user.lastname,
95 'email': user.email })
96 return result
97
80
98
81 @HasPermissionAllDecorator('hg.admin')
99 @HasPermissionAllDecorator('hg.admin')
82 def create_users_group(self, apiuser, name, active):
100 def create_users_group(self, apiuser, name, active=True):
83 """
101 """
84 Creates an new usergroup
102 Creates an new usergroup
85
103
86 :param name:
104 :param name:
87 :param active:
105 :param active:
88 """
106 """
@@ -95,4 +113,81 b' class ApiController(JSONRPCController):'
95 except Exception:
113 except Exception:
96 log.error(traceback.format_exc())
114 log.error(traceback.format_exc())
97 raise JSONRPCError('failed to create group %s' % name)
115 raise JSONRPCError('failed to create group %s' % name)
98 No newline at end of file
116
117 @HasPermissionAllDecorator('hg.admin')
118 def list_users_groups(self, apiuser):
119 """"
120 Lists all users groups
121
122 :param apiuser
123 """
124 result = []
125 for users_group in UsersGroup.getAll():
126 result.append({ 'name': users_group.name })
127 return result
128
129
130 @HasPermissionAllDecorator('hg.admin')
131 def add_user_to_group(self, apiuser, user_name, group_name):
132 """"
133 Add a user to a group
134
135 :param apiuser
136 :param user_name
137 :param group_name
138 """
139
140 users_group = UsersGroup.get_by_group_name(group_name)
141 if not users_group:
142 raise JSONRPCError('unknown users group %s' % group_name)
143
144 user = User.by_username(user_name)
145 if not user:
146 raise JSONRPCError('unknown user %s' % user_name)
147
148 try:
149 UsersGroupMember.create(user, users_group)
150 except Exception:
151 log.error(traceback.format_exc())
152 raise JSONRPCError('failed to create users group member')
153
154 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
155 def create_repo(self, apiuser, name, owner_name, description=None, repo_type='hg', \
156 private=False, group_name=None):
157 """
158 Create a repository
159
160 :param apiuser
161 :param name
162 :param description
163 :param type
164 :param private
165 :param owner_name
166 :param group_name
167 :param clone
168 """
169
170 if group_name:
171 group = Group.get_by_group_name(group_name)
172 if group is None:
173 raise JSONRPCError('unknown group %s' % group_name)
174 else:
175 group = None
176
177 owner = User.by_username(owner_name)
178 if owner is None:
179 raise JSONRPCError('unknown user %s' % owner)
180
181 try:
182 RepoModel().create({ "repo_name" : name,
183 "repo_name_full" : name,
184 "description" : description,
185 "private" : private,
186 "repo_type" : repo_type,
187 "repo_group" : group,
188 "clone_uri" : None }, owner)
189 except Exception:
190 log.error(traceback.format_exc())
191 raise JSONRPCError('failed to create repository %s' % name)
192
193
@@ -57,24 +57,24 b' log = logging.getLogger(__name__)'
57 class ModelSerializer(json.JSONEncoder):
57 class ModelSerializer(json.JSONEncoder):
58 """
58 """
59 Simple Serializer for JSON,
59 Simple Serializer for JSON,
60
60
61 usage::
61 usage::
62
62
63 to make object customized for serialization implement a __json__
63 to make object customized for serialization implement a __json__
64 method that will return a dict for serialization into json
64 method that will return a dict for serialization into json
65
65
66 example::
66 example::
67
67
68 class Task(object):
68 class Task(object):
69
69
70 def __init__(self, name, value):
70 def __init__(self, name, value):
71 self.name = name
71 self.name = name
72 self.value = value
72 self.value = value
73
73
74 def __json__(self):
74 def __json__(self):
75 return dict(name=self.name,
75 return dict(name=self.name,
76 value=self.value)
76 value=self.value)
77
77
78 """
78 """
79
79
80 def default(self, obj):
80 def default(self, obj):
@@ -125,11 +125,15 b' class BaseModel(object):'
125
125
126 @classmethod
126 @classmethod
127 def get(cls, id_):
127 def get(cls, id_):
128 return Session.query(cls).get(id_)
128 return cls.query().get(id_)
129
130 @classmethod
131 def getAll(cls):
132 return cls.query().all()
129
133
130 @classmethod
134 @classmethod
131 def delete(cls, id_):
135 def delete(cls, id_):
132 obj = Session.query(cls).get(id_)
136 obj = cls.query().get(id_)
133 Session.delete(obj)
137 Session.delete(obj)
134 Session.commit()
138 Session.commit()
135
139
@@ -152,13 +156,13 b' class RhodeCodeSettings(Base, BaseModel)'
152
156
153 @classmethod
157 @classmethod
154 def get_by_name(cls, ldap_key):
158 def get_by_name(cls, ldap_key):
155 return Session.query(cls)\
159 return cls.query()\
156 .filter(cls.app_settings_name == ldap_key).scalar()
160 .filter(cls.app_settings_name == ldap_key).scalar()
157
161
158 @classmethod
162 @classmethod
159 def get_app_settings(cls, cache=False):
163 def get_app_settings(cls, cache=False):
160
164
161 ret = Session.query(cls)
165 ret = cls.query()
162
166
163 if cache:
167 if cache:
164 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
168 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
@@ -174,7 +178,7 b' class RhodeCodeSettings(Base, BaseModel)'
174
178
175 @classmethod
179 @classmethod
176 def get_ldap_settings(cls, cache=False):
180 def get_ldap_settings(cls, cache=False):
177 ret = Session.query(cls)\
181 ret = cls.query()\
178 .filter(cls.app_settings_name.startswith('ldap_'))\
182 .filter(cls.app_settings_name.startswith('ldap_'))\
179 .all()
183 .all()
180 fd = {}
184 fd = {}
@@ -204,7 +208,7 b' class RhodeCodeUi(Base, BaseModel):'
204
208
205 @classmethod
209 @classmethod
206 def get_by_key(cls, key):
210 def get_by_key(cls, key):
207 return Session.query(cls).filter(cls.ui_key == key)
211 return cls.query().filter(cls.ui_key == key)
208
212
209
213
210 @classmethod
214 @classmethod
@@ -282,14 +286,13 b' class User(Base, BaseModel):'
282 @classmethod
286 @classmethod
283 def by_username(cls, username, case_insensitive=False):
287 def by_username(cls, username, case_insensitive=False):
284 if case_insensitive:
288 if case_insensitive:
285 return Session.query(cls).filter(cls.username.like(username)).one()
289 return cls.query().filter(cls.username.like(username)).one()
286 else:
290 else:
287 return Session.query(cls).filter(cls.username == username).one()
291 return cls.query().filter(cls.username == username).one()
288
292
289 @classmethod
293 @classmethod
290 def get_by_api_key(cls, api_key):
294 def get_by_api_key(cls, api_key):
291 return Session.query(cls).filter(cls.api_key == api_key).one()
295 return cls.query().filter(cls.api_key == api_key).one()
292
293
296
294 def update_lastlogin(self):
297 def update_lastlogin(self):
295 """Update user lastlogin"""
298 """Update user lastlogin"""
@@ -302,7 +305,7 b' class User(Base, BaseModel):'
302 @classmethod
305 @classmethod
303 def create(cls, form_data):
306 def create(cls, form_data):
304 from rhodecode.lib.auth import get_crypt_password
307 from rhodecode.lib.auth import get_crypt_password
305
308
306 try:
309 try:
307 new_user = cls()
310 new_user = cls()
308 for k, v in form_data.items():
311 for k, v in form_data.items():
@@ -354,11 +357,11 b' class UsersGroup(Base, BaseModel):'
354 @classmethod
357 @classmethod
355 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
358 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
356 if case_insensitive:
359 if case_insensitive:
357 gr = Session.query(cls)\
360 gr = cls.query()\
358 .filter(cls.users_group_name.ilike(group_name))
361 .filter(cls.users_group_name.ilike(group_name))
359 else:
362 else:
360 gr = Session.query(UsersGroup)\
363 gr = cls.query()\
361 .filter(UsersGroup.users_group_name == group_name)
364 .filter(cls.users_group_name == group_name)
362 if cache:
365 if cache:
363 gr = gr.options(FromCache("sql_cache_short",
366 gr = gr.options(FromCache("sql_cache_short",
364 "get_user_%s" % group_name))
367 "get_user_%s" % group_name))
@@ -367,7 +370,7 b' class UsersGroup(Base, BaseModel):'
367
370
368 @classmethod
371 @classmethod
369 def get(cls, users_group_id, cache=False):
372 def get(cls, users_group_id, cache=False):
370 users_group = Session.query(cls)
373 users_group = cls.query()
371 if cache:
374 if cache:
372 users_group = users_group.options(FromCache("sql_cache_short",
375 users_group = users_group.options(FromCache("sql_cache_short",
373 "get_users_group_%s" % users_group_id))
376 "get_users_group_%s" % users_group_id))
@@ -451,6 +454,22 b' class UsersGroupMember(Base, BaseModel):'
451 self.users_group_id = gr_id
454 self.users_group_id = gr_id
452 self.user_id = u_id
455 self.user_id = u_id
453
456
457 @classmethod
458 def create(cls, user, users_group):
459 try:
460 users_group_member = cls()
461 users_group_member.user = user
462 users_group_member.users_group = users_group
463
464 Session.add(users_group_member)
465 Session.commit()
466 return users_group_member
467 except:
468 log.error(traceback.format_exc())
469 Session.rollback()
470 raise
471
472
454 class Repository(Base, BaseModel):
473 class Repository(Base, BaseModel):
455 __tablename__ = 'repositories'
474 __tablename__ = 'repositories'
456 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
475 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
@@ -485,9 +504,31 b' class Repository(Base, BaseModel):'
485 return "<%s('%s:%s')>" % (self.__class__.__name__,
504 return "<%s('%s:%s')>" % (self.__class__.__name__,
486 self.repo_id, self.repo_name)
505 self.repo_id, self.repo_name)
487
506
507 @staticmethod
508 def create(name, description, repo_type, private, owner, group, clone):
509 try:
510 repo = Repository()
511 repo.repo_name = name
512 repo.clone_uri = clone
513 repo.repo_type = repo_type
514 repo.user = owner
515 repo.private = private
516 repo.description = description
517 repo.group = group
518
519 Session.add(repo)
520 Session.commit()
521
522 RepoToPerm.create(repo, owner, Permission.get_by_name('repository.write'))
523 return repo
524 except:
525 log.error(traceback.format_exc())
526 Session.rollback()
527 raise
528
488 @classmethod
529 @classmethod
489 def by_repo_name(cls, repo_name):
530 def by_repo_name(cls, repo_name):
490 q = Session.query(cls).filter(cls.repo_name == repo_name)
531 q = cls.query().filter(cls.repo_name == repo_name)
491
532
492 q = q.options(joinedload(Repository.fork))\
533 q = q.options(joinedload(Repository.fork))\
493 .options(joinedload(Repository.user))\
534 .options(joinedload(Repository.user))\
@@ -497,7 +538,7 b' class Repository(Base, BaseModel):'
497
538
498 @classmethod
539 @classmethod
499 def get_repo_forks(cls, repo_id):
540 def get_repo_forks(cls, repo_id):
500 return Session.query(cls).filter(Repository.fork_id == repo_id)
541 return cls.query().filter(Repository.fork_id == repo_id)
501
542
502 @classmethod
543 @classmethod
503 def base_path(cls):
544 def base_path(cls):
@@ -541,7 +582,7 b' class Repository(Base, BaseModel):'
541 Returns base full path for that repository means where it actually
582 Returns base full path for that repository means where it actually
542 exists on a filesystem
583 exists on a filesystem
543 """
584 """
544 q = Session.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/')
585 q = RhodeCodeUi.query().filter(RhodeCodeUi.ui_key == '/')
545 q.options(FromCache("sql_cache_short", "repository_repo_path"))
586 q.options(FromCache("sql_cache_short", "repository_repo_path"))
546 return q.one().ui_value
587 return q.one().ui_value
547
588
@@ -569,7 +610,7 b' class Repository(Base, BaseModel):'
569 baseui._tcfg = config.config()
610 baseui._tcfg = config.config()
570
611
571
612
572 ret = Session.query(RhodeCodeUi)\
613 ret = RhodeCodeUi.query()\
573 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
614 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
574
615
575 hg_ui = ret
616 hg_ui = ret
@@ -624,7 +665,7 b' class Repository(Base, BaseModel):'
624 None otherwise. `cache_active = False` means that this cache
665 None otherwise. `cache_active = False` means that this cache
625 state is not valid and needs to be invalidated
666 state is not valid and needs to be invalidated
626 """
667 """
627 return Session.query(CacheInvalidation)\
668 return CacheInvalidation.query()\
628 .filter(CacheInvalidation.cache_key == self.repo_name)\
669 .filter(CacheInvalidation.cache_key == self.repo_name)\
629 .filter(CacheInvalidation.cache_active == False)\
670 .filter(CacheInvalidation.cache_active == False)\
630 .scalar()
671 .scalar()
@@ -633,7 +674,7 b' class Repository(Base, BaseModel):'
633 """
674 """
634 set a cache for invalidation for this instance
675 set a cache for invalidation for this instance
635 """
676 """
636 inv = Session.query(CacheInvalidation)\
677 inv = CacheInvalidation.query()\
637 .filter(CacheInvalidation.cache_key == self.repo_name)\
678 .filter(CacheInvalidation.cache_key == self.repo_name)\
638 .scalar()
679 .scalar()
639
680
@@ -721,6 +762,19 b' class Group(Base, BaseModel):'
721 def url_sep(cls):
762 def url_sep(cls):
722 return '/'
763 return '/'
723
764
765 @classmethod
766 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
767 if case_insensitive:
768 gr = cls.query()\
769 .filter(cls.group_name.ilike(group_name))
770 else:
771 gr = cls.query()\
772 .filter(cls.group_name == group_name)
773 if cache:
774 gr = gr.options(FromCache("sql_cache_short",
775 "get_group_%s" % group_name))
776 return gr.scalar()
777
724 @property
778 @property
725 def parents(self):
779 def parents(self):
726 parents_recursion_limit = 5
780 parents_recursion_limit = 5
@@ -747,7 +801,7 b' class Group(Base, BaseModel):'
747
801
748 @property
802 @property
749 def children(self):
803 def children(self):
750 return Session.query(Group).filter(Group.parent_group == self)
804 return Group.query().filter(Group.parent_group == self)
751
805
752 @property
806 @property
753 def full_path(self):
807 def full_path(self):
@@ -756,7 +810,7 b' class Group(Base, BaseModel):'
756
810
757 @property
811 @property
758 def repositories(self):
812 def repositories(self):
759 return Session.query(Repository).filter(Repository.group == self)
813 return Repository.query().filter(Repository.group == self)
760
814
761 @property
815 @property
762 def repositories_recursive_count(self):
816 def repositories_recursive_count(self):
@@ -784,7 +838,11 b' class Permission(Base, BaseModel):'
784
838
785 @classmethod
839 @classmethod
786 def get_by_key(cls, key):
840 def get_by_key(cls, key):
787 return Session.query(cls).filter(cls.permission_name == key).scalar()
841 return cls.query().filter(cls.permission_name == key).scalar()
842
843 @classmethod
844 def get_by_name(cls, name):
845 return cls.query().filter(cls.permission_name == name).one()
788
846
789 class RepoToPerm(Base, BaseModel):
847 class RepoToPerm(Base, BaseModel):
790 __tablename__ = 'repo_to_perm'
848 __tablename__ = 'repo_to_perm'
@@ -798,6 +856,23 b' class RepoToPerm(Base, BaseModel):'
798 permission = relationship('Permission')
856 permission = relationship('Permission')
799 repository = relationship('Repository')
857 repository = relationship('Repository')
800
858
859 @staticmethod
860 def create(repo, user, p):
861 try:
862 perm = RepoToPerm()
863 perm.repository = repo
864 perm.user = user
865 perm.permission = p
866
867 Session.add(perm)
868 Session.commit()
869
870 return perm
871 except:
872 log.error(traceback.format_exc())
873 Session.rollback()
874 raise
875
801 class UserToPerm(Base, BaseModel):
876 class UserToPerm(Base, BaseModel):
802 __tablename__ = 'user_to_perm'
877 __tablename__ = 'user_to_perm'
803 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
878 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
@@ -813,7 +888,7 b' class UserToPerm(Base, BaseModel):'
813 if not isinstance(perm, Permission):
888 if not isinstance(perm, Permission):
814 raise Exception('perm needs to be an instance of Permission class')
889 raise Exception('perm needs to be an instance of Permission class')
815
890
816 return Session.query(cls).filter(cls.user_id == user_id)\
891 return cls.query().filter(cls.user_id == user_id)\
817 .filter(cls.permission == perm).scalar() is not None
892 .filter(cls.permission == perm).scalar() is not None
818
893
819 @classmethod
894 @classmethod
@@ -837,7 +912,7 b' class UserToPerm(Base, BaseModel):'
837 raise Exception('perm needs to be an instance of Permission class')
912 raise Exception('perm needs to be an instance of Permission class')
838
913
839 try:
914 try:
840 Session.query(cls).filter(cls.user_id == user_id)\
915 cls.query().filter(cls.user_id == user_id)\
841 .filter(cls.permission == perm).delete()
916 .filter(cls.permission == perm).delete()
842 Session.commit()
917 Session.commit()
843 except:
918 except:
@@ -873,7 +948,7 b' class UsersGroupToPerm(Base, BaseModel):'
873 if not isinstance(perm, Permission):
948 if not isinstance(perm, Permission):
874 raise Exception('perm needs to be an instance of Permission class')
949 raise Exception('perm needs to be an instance of Permission class')
875
950
876 return Session.query(cls).filter(cls.users_group_id ==
951 return cls.query().filter(cls.users_group_id ==
877 users_group_id)\
952 users_group_id)\
878 .filter(cls.permission == perm)\
953 .filter(cls.permission == perm)\
879 .scalar() is not None
954 .scalar() is not None
@@ -899,7 +974,7 b' class UsersGroupToPerm(Base, BaseModel):'
899 raise Exception('perm needs to be an instance of Permission class')
974 raise Exception('perm needs to be an instance of Permission class')
900
975
901 try:
976 try:
902 Session.query(cls).filter(cls.users_group_id == users_group_id)\
977 cls.query().filter(cls.users_group_id == users_group_id)\
903 .filter(cls.permission == perm).delete()
978 .filter(cls.permission == perm).delete()
904 Session.commit()
979 Session.commit()
905 except:
980 except:
@@ -951,7 +1026,7 b' class UserFollowing(Base, BaseModel):'
951
1026
952 @classmethod
1027 @classmethod
953 def get_repo_followers(cls, repo_id):
1028 def get_repo_followers(cls, repo_id):
954 return Session.query(cls).filter(cls.follows_repo_id == repo_id)
1029 return cls.query().filter(cls.follows_repo_id == repo_id)
955
1030
956 class CacheInvalidation(Base, BaseModel):
1031 class CacheInvalidation(Base, BaseModel):
957 __tablename__ = 'cache_invalidation'
1032 __tablename__ = 'cache_invalidation'
General Comments 0
You need to be logged in to leave comments. Login now