pull/1075/head
Piero Toffanin 2021-10-18 16:42:13 -04:00
rodzic 2beaad3910
commit 0db85bba66
5 zmienionych plików z 92 dodań i 13 usunięć

Wyświetl plik

@ -70,9 +70,9 @@ class ProjectViewSet(viewsets.ModelViewSet):
result = []
perms = get_users_with_perms(project, attach_perms=True)
perms = get_users_with_perms(project, attach_perms=True, with_group_users=False)
for user in perms:
result.append({'user': user.username,
result.append({'username': user.username,
'owner': project.owner == user,
'permissions': normalized_perm_names(perms[user])})

Wyświetl plik

@ -12,12 +12,16 @@ from rest_framework_jwt.views import obtain_jwt_token
from .tiler import TileJson, Bounds, Metadata, Tiles, Export
from .potree import Scene, CameraView
from .workers import CheckTask, GetTaskResult
from .users import UserViewSet
from webodm import settings
router = routers.DefaultRouter()
router.register(r'projects', ProjectViewSet)
router.register(r'processingnodes', ProcessingNodeViewSet)
router.register(r'presets', PresetViewSet, base_name='presets')
if settings.ENABLE_USERS_API:
router.register(r'users', UserViewSet, base_name='users')
tasks_router = routers.NestedSimpleRouter(router, r'projects', lookup='project')
tasks_router.register(r'tasks', TaskViewSet, base_name='projects-tasks')

33
app/api/users.py 100644
Wyświetl plik

@ -0,0 +1,33 @@
from django.contrib.auth.models import User
from rest_framework import serializers, viewsets, mixins, status, exceptions, permissions
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['username', 'email']
class UserViewSet(mixins.ListModelMixin, viewsets.GenericViewSet):
serializer_class = UserSerializer
permission_classes = [permissions.IsAuthenticated]
# Disable pagination when not requesting any page
def paginate_queryset(self, queryset):
if self.paginator and self.request.query_params.get(self.paginator.page_query_param, None) is None:
return None
return super().paginate_queryset(queryset)
def get_queryset(self):
queryset = User.objects.all()
search = self.request.query_params.get('search', None)
if search is not None:
queryset = queryset.filter(username__istartswith=search) | queryset.filter(email__istartswith=search)
limit = self.request.query_params.get('limit', None)
if limit is not None:
try:
queryset = queryset[:abs(int(limit))]
except ValueError:
raise exceptions.ValidationError(detail="Invalid query parameters")
return queryset

Wyświetl plik

@ -25,7 +25,8 @@ class EditPermissionsPanel extends React.Component {
error: "",
loading: false,
permissions: [],
validUsernames: []
validUsernames: {},
validatingUser: false
};
this.backgroundFailedColor = Css.getValue('btn-danger', 'backgroundColor');
@ -36,7 +37,8 @@ class EditPermissionsPanel extends React.Component {
this.permsRequest =
$.getJSON(`/api/projects/${this.props.projectId}/permissions/`, json => {
let validUsernames = json.map(p => p.user);
let validUsernames = {};
json.forEach(p => validUsernames[p.username] = true);
this.setState({validUsernames, permissions: json});
})
.fail(() => {
@ -47,12 +49,43 @@ class EditPermissionsPanel extends React.Component {
});
}
validateUsername = (username) => {
if (this.validateReq){
this.validateReq.abort();
this.validateReq = null;
}
if (this.validateTimeout){
clearTimeout(this.validateTimeout);
this.validateTimeout = null;
}
this.setState({validatingUser: true});
this.validateTimeout = setTimeout(() => {
this.validateReq = $.getJSON(`/api/users/?limit=30&search=${encodeURIComponent(username)}`)
.done((json) => {
json.forEach(u => {
this.state.validUsernames[u.username] = true;
});
this.setState({validUsernames: this.state.validUsernames});
}).fail(() => {
}).always(() => {
this.setState({validatingUser: false});
});
}, 300);
}
componentDidMount(){
if (!this.props.lazyLoad) this.loadPermissions();
}
componentWillUnmount(){
if (this.permsRequest) this.permsRequest.abort();
if (this.validateReq) this.validateReq.abort();
if (this.validateTimeout) clearTimeout(this.validateTimeout);
}
handleChangePermissionRole = e => {
@ -61,7 +94,9 @@ class EditPermissionsPanel extends React.Component {
handleChangePermissionUser = perm => {
return e => {
perm.user = e.target.value;
perm.username = e.target.value;
this.validateUsername(perm.username);
// Update
this.setState({permissions: this.state.permissions});
@ -87,13 +122,13 @@ class EditPermissionsPanel extends React.Component {
}
getColorFor = (username) => {
if (this.state.validUsernames.indexOf(username) !== -1) return "";
if (this.state.validatingUser || this.state.validUsernames[username]) return "";
else return this.backgroundFailedColor;
}
addNewPermission = () => {
this.setState(update(this.state, {
permissions: {$push: [{user: "", permissions: ["view"]}]}
permissions: {$push: [{username: "", permissions: ["view"]}]}
}));
setTimeout(() => {
@ -110,18 +145,19 @@ class EditPermissionsPanel extends React.Component {
}
render() {
const permissions = this.state.permissions.map((p, i) => <div key={i}>
const permissions = this.state.permissions.map((p, i) => <form autoComplete="off" key={i}>
<div className="permission">
<div className="username-container">
<i className="fa fa-user user-indicator"/>
<input
style={{color: this.getColorFor(p.user)}}
style={{color: this.getColorFor(p.username)}}
onChange={this.handleChangePermissionUser(p)}
type="text"
type="text"
autoComplete="off"
disabled={p.owner}
value={p.user}
value={p.username}
className="form-control username"
placeholder={_("Username / e-mail")}
placeholder={_("Username")}
ref={(domNode) => this.lastTextbox = domNode} />
</div>
<div className="remove">
@ -133,7 +169,7 @@ class EditPermissionsPanel extends React.Component {
</select>
</div>
</div>
</div>);
</form>);
return (
<div className="edit-permissions-panel">

Wyświetl plik

@ -69,6 +69,12 @@ SINGLE_USER_MODE = False
# URL to redirect to if there are no processing nodes when visiting the dashboard
PROCESSING_NODES_ONBOARDING = None
# Enable the /api/users endpoint which is used for autocompleting
# usernames when handling project permissions. This can be disabled
# for security reasons if you don't want to let authenticated users
# retrieve the user list.
ENABLE_USERS_API = True
# Enable desktop mode. In desktop mode some styling changes
# are applied to make the application look nicer on desktop
# as well as disabling certain features (e.g. sharing)