Skip to content
Snippets Groups Projects
Commit 9745b57f authored by Eliot Berriot's avatar Eliot Berriot
Browse files

Merge branch '236-default-permission' into 'develop'

Resolve "Implement default permissions for logged in users"

Closes #236

See merge request funkwhale/funkwhale!213
parents c6cd3abf e9fc220e
No related branches found
No related tags found
No related merge requests found
from django.conf import settings from django.conf import settings
from django import forms
from dynamic_preferences import serializers
from dynamic_preferences import types
from dynamic_preferences.registries import global_preferences_registry from dynamic_preferences.registries import global_preferences_registry
...@@ -10,3 +14,38 @@ class DefaultFromSettingMixin(object): ...@@ -10,3 +14,38 @@ class DefaultFromSettingMixin(object):
def get(pref): def get(pref):
manager = global_preferences_registry.manager() manager = global_preferences_registry.manager()
return manager[pref] return manager[pref]
class StringListSerializer(serializers.BaseSerializer):
separator = ','
sort = True
@classmethod
def to_db(cls, value, **kwargs):
if not value:
return
if type(value) not in [list, tuple]:
raise cls.exception(
"Cannot serialize, value {} is not a list or a tuple".format(
value))
if cls.sort:
value = sorted(value)
return cls.separator.join(value)
@classmethod
def to_python(cls, value, **kwargs):
if not value:
return []
return value.split(',')
class StringListPreference(types.BasePreferenceType):
serializer = StringListSerializer
field_class = forms.MultipleChoiceField
def get_api_additional_data(self):
d = super(StringListPreference, self).get_api_additional_data()
d['choices'] = self.get('choices')
return d
from dynamic_preferences import types from dynamic_preferences import types
from dynamic_preferences.registries import global_preferences_registry from dynamic_preferences.registries import global_preferences_registry
from funkwhale_api.common import preferences as common_preferences
from . import models
users = types.Section('users') users = types.Section('users')
...@@ -14,3 +18,23 @@ class RegistrationEnabled(types.BooleanPreference): ...@@ -14,3 +18,23 @@ class RegistrationEnabled(types.BooleanPreference):
help_text = ( help_text = (
'When enabled, new users will be able to register on this instance.' 'When enabled, new users will be able to register on this instance.'
) )
@global_preferences_registry.register
class DefaultPermissions(common_preferences.StringListPreference):
show_in_api = True
section = users
name = 'default_permissions'
default = []
verbose_name = 'Default permissions'
help_text = (
'A list of default preferences to give to all registered users.'
)
choices = [
(k, c['label'])
for k, c in models.PERMISSIONS_CONFIGURATION.items()
]
field_kwargs = {
'choices': choices,
'required': False,
}
...@@ -13,18 +13,33 @@ from django.utils.encoding import python_2_unicode_compatible ...@@ -13,18 +13,33 @@ from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from funkwhale_api.common import fields from funkwhale_api.common import fields
from funkwhale_api.common import preferences
def get_token(): def get_token():
return binascii.b2a_hex(os.urandom(15)).decode('utf-8') return binascii.b2a_hex(os.urandom(15)).decode('utf-8')
PERMISSIONS = [ PERMISSIONS_CONFIGURATION = {
'federation', 'federation': {
'library', 'label': 'Manage library federation',
'settings', 'help_text': 'Follow other instances, accept/deny library follow requests...',
'upload', },
] 'library': {
'label': 'Manage library',
'help_text': 'Manage library',
},
'settings': {
'label': 'Manage instance-level settings',
'help_text': '',
},
'upload': {
'label': 'Upload new content to the library',
'help_text': '',
},
}
PERMISSIONS = sorted(PERMISSIONS_CONFIGURATION.keys())
@python_2_unicode_compatible @python_2_unicode_compatible
...@@ -48,27 +63,34 @@ class User(AbstractUser): ...@@ -48,27 +63,34 @@ class User(AbstractUser):
# permissions # permissions
permission_federation = models.BooleanField( permission_federation = models.BooleanField(
'Manage library federation', PERMISSIONS_CONFIGURATION['federation']['label'],
help_text='Follow other instances, accept/deny library follow requests...', help_text=PERMISSIONS_CONFIGURATION['federation']['help_text'],
default=False) default=False)
permission_library = models.BooleanField( permission_library = models.BooleanField(
'Manage library', PERMISSIONS_CONFIGURATION['library']['label'],
help_text='Manage library', help_text=PERMISSIONS_CONFIGURATION['library']['help_text'],
default=False) default=False)
permission_settings = models.BooleanField( permission_settings = models.BooleanField(
'Manage instance-level settings', PERMISSIONS_CONFIGURATION['settings']['label'],
help_text=PERMISSIONS_CONFIGURATION['settings']['help_text'],
default=False) default=False)
permission_upload = models.BooleanField( permission_upload = models.BooleanField(
'Upload new content to the library', PERMISSIONS_CONFIGURATION['upload']['label'],
help_text=PERMISSIONS_CONFIGURATION['upload']['help_text'],
default=False) default=False)
def __str__(self): def __str__(self):
return self.username return self.username
def get_permissions(self): def get_permissions(self):
defaults = preferences.get('users__default_permissions')
perms = {} perms = {}
for p in PERMISSIONS: for p in PERMISSIONS:
v = self.is_superuser or getattr(self, 'permission_{}'.format(p)) v = (
self.is_superuser or
getattr(self, 'permission_{}'.format(p)) or
p in defaults
)
perms[p] = v perms[p] = v
return perms return perms
......
import pytest
from dynamic_preferences.registries import global_preferences_registry
from funkwhale_api.common import preferences as common_preferences
@pytest.fixture
def string_list_pref(preferences):
@global_preferences_registry.register
class P(common_preferences.StringListPreference):
default = ['hello']
section = 'test'
name = 'string_list'
yield
del global_preferences_registry['test']['string_list']
@pytest.mark.parametrize('input,output', [
(['a', 'b', 'c'], 'a,b,c'),
(['a', 'c', 'b'], 'a,b,c'),
(('a', 'c', 'b'), 'a,b,c'),
([], None),
])
def test_string_list_serializer_to_db(input, output):
s = common_preferences.StringListSerializer.to_db(input) == output
@pytest.mark.parametrize('input,output', [
('a,b,c', ['a', 'b', 'c'], ),
(None, []),
('', []),
])
def test_string_list_serializer_to_python(input, output):
s = common_preferences.StringListSerializer.to_python(input) == output
def test_string_list_pref_default(string_list_pref, preferences):
assert preferences['test__string_list'] == ['hello']
def test_string_list_pref_set(string_list_pref, preferences):
preferences['test__string_list'] = ['world', 'hello']
assert preferences['test__string_list'] == ['hello', 'world']
...@@ -41,6 +41,17 @@ def test_get_permissions_regular(factories): ...@@ -41,6 +41,17 @@ def test_get_permissions_regular(factories):
assert perms[p] is False assert perms[p] is False
def test_get_permissions_default(factories, preferences):
preferences['users__default_permissions'] = ['upload', 'federation']
user = factories['users.User']()
perms = user.get_permissions()
assert perms['upload'] is True
assert perms['federation'] is True
assert perms['library'] is False
assert perms['settings'] is False
@pytest.mark.parametrize('args,perms,expected', [ @pytest.mark.parametrize('args,perms,expected', [
({'is_superuser': True}, ['federation', 'library'], True), ({'is_superuser': True}, ['federation', 'library'], True),
({'is_superuser': False}, ['federation'], False), ({'is_superuser': False}, ['federation'], False),
......
Admins can now configure default permissions that will be granted to all
registered users (#236)
...@@ -50,6 +50,13 @@ ...@@ -50,6 +50,13 @@
<label :for="setting.identifier">{{ setting.verbose_name }}</label> <label :for="setting.identifier">{{ setting.verbose_name }}</label>
<p v-if="setting.help_text">{{ setting.help_text }}</p> <p v-if="setting.help_text">{{ setting.help_text }}</p>
</div> </div>
<select
v-else-if="setting.field.class === 'MultipleChoiceField'"
v-model="values[setting.identifier]"
multiple
class="ui search selection dropdown">
<option v-for="v in setting.additional_data.choices" :value="v[0]">{{ v[1] }}</option>
</select>
</div> </div>
<button <button
type="submit" type="submit"
......
...@@ -81,6 +81,8 @@ axios.interceptors.response.use(function (response) { ...@@ -81,6 +81,8 @@ axios.interceptors.response.use(function (response) {
} }
if (error.response.status === 404) { if (error.response.status === 404) {
error.backendErrors.push('Resource not found') error.backendErrors.push('Resource not found')
} else if (error.response.status === 403) {
error.backendErrors.push('Permission denied')
} else if (error.response.status === 500) { } else if (error.response.status === 500) {
error.backendErrors.push('A server error occured') error.backendErrors.push('A server error occured')
} else if (error.response.data) { } else if (error.response.data) {
......
...@@ -51,12 +51,12 @@ export default { ...@@ -51,12 +51,12 @@ export default {
if (self.$store.state.route.hash) { if (self.$store.state.route.hash) {
self.scrollTo(self.$store.state.route.hash.substr(1)) self.scrollTo(self.$store.state.route.hash.substr(1))
} }
$('select.dropdown').dropdown()
}) })
}) })
}, },
methods: { methods: {
scrollTo (id) { scrollTo (id) {
console.log(id, 'hello')
this.current = id this.current = id
document.getElementById(id).scrollIntoView() document.getElementById(id).scrollIntoView()
}, },
...@@ -86,7 +86,8 @@ export default { ...@@ -86,7 +86,8 @@ export default {
id: 'users', id: 'users',
settings: [ settings: [
'users__registration_enabled', 'users__registration_enabled',
'common__api_authentication_required' 'common__api_authentication_required',
'users__default_permissions'
] ]
}, },
{ {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment