kopia lustrzana https://github.com/OpenDroneMap/WebODM
Add /api/users
rodzic
2beaad3910
commit
0db85bba66
|
@ -70,9 +70,9 @@ class ProjectViewSet(viewsets.ModelViewSet):
|
||||||
|
|
||||||
result = []
|
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:
|
for user in perms:
|
||||||
result.append({'user': user.username,
|
result.append({'username': user.username,
|
||||||
'owner': project.owner == user,
|
'owner': project.owner == user,
|
||||||
'permissions': normalized_perm_names(perms[user])})
|
'permissions': normalized_perm_names(perms[user])})
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,16 @@ from rest_framework_jwt.views import obtain_jwt_token
|
||||||
from .tiler import TileJson, Bounds, Metadata, Tiles, Export
|
from .tiler import TileJson, Bounds, Metadata, Tiles, Export
|
||||||
from .potree import Scene, CameraView
|
from .potree import Scene, CameraView
|
||||||
from .workers import CheckTask, GetTaskResult
|
from .workers import CheckTask, GetTaskResult
|
||||||
|
from .users import UserViewSet
|
||||||
|
from webodm import settings
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
router.register(r'projects', ProjectViewSet)
|
router.register(r'projects', ProjectViewSet)
|
||||||
router.register(r'processingnodes', ProcessingNodeViewSet)
|
router.register(r'processingnodes', ProcessingNodeViewSet)
|
||||||
router.register(r'presets', PresetViewSet, base_name='presets')
|
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 = routers.NestedSimpleRouter(router, r'projects', lookup='project')
|
||||||
tasks_router.register(r'tasks', TaskViewSet, base_name='projects-tasks')
|
tasks_router.register(r'tasks', TaskViewSet, base_name='projects-tasks')
|
||||||
|
|
|
@ -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
|
|
@ -25,7 +25,8 @@ class EditPermissionsPanel extends React.Component {
|
||||||
error: "",
|
error: "",
|
||||||
loading: false,
|
loading: false,
|
||||||
permissions: [],
|
permissions: [],
|
||||||
validUsernames: []
|
validUsernames: {},
|
||||||
|
validatingUser: false
|
||||||
};
|
};
|
||||||
|
|
||||||
this.backgroundFailedColor = Css.getValue('btn-danger', 'backgroundColor');
|
this.backgroundFailedColor = Css.getValue('btn-danger', 'backgroundColor');
|
||||||
|
@ -36,7 +37,8 @@ class EditPermissionsPanel extends React.Component {
|
||||||
|
|
||||||
this.permsRequest =
|
this.permsRequest =
|
||||||
$.getJSON(`/api/projects/${this.props.projectId}/permissions/`, json => {
|
$.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});
|
this.setState({validUsernames, permissions: json});
|
||||||
})
|
})
|
||||||
.fail(() => {
|
.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(){
|
componentDidMount(){
|
||||||
if (!this.props.lazyLoad) this.loadPermissions();
|
if (!this.props.lazyLoad) this.loadPermissions();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount(){
|
componentWillUnmount(){
|
||||||
if (this.permsRequest) this.permsRequest.abort();
|
if (this.permsRequest) this.permsRequest.abort();
|
||||||
|
if (this.validateReq) this.validateReq.abort();
|
||||||
|
if (this.validateTimeout) clearTimeout(this.validateTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
handleChangePermissionRole = e => {
|
handleChangePermissionRole = e => {
|
||||||
|
@ -61,7 +94,9 @@ class EditPermissionsPanel extends React.Component {
|
||||||
|
|
||||||
handleChangePermissionUser = perm => {
|
handleChangePermissionUser = perm => {
|
||||||
return e => {
|
return e => {
|
||||||
perm.user = e.target.value;
|
perm.username = e.target.value;
|
||||||
|
|
||||||
|
this.validateUsername(perm.username);
|
||||||
|
|
||||||
// Update
|
// Update
|
||||||
this.setState({permissions: this.state.permissions});
|
this.setState({permissions: this.state.permissions});
|
||||||
|
@ -87,13 +122,13 @@ class EditPermissionsPanel extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
getColorFor = (username) => {
|
getColorFor = (username) => {
|
||||||
if (this.state.validUsernames.indexOf(username) !== -1) return "";
|
if (this.state.validatingUser || this.state.validUsernames[username]) return "";
|
||||||
else return this.backgroundFailedColor;
|
else return this.backgroundFailedColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
addNewPermission = () => {
|
addNewPermission = () => {
|
||||||
this.setState(update(this.state, {
|
this.setState(update(this.state, {
|
||||||
permissions: {$push: [{user: "", permissions: ["view"]}]}
|
permissions: {$push: [{username: "", permissions: ["view"]}]}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
@ -110,18 +145,19 @@ class EditPermissionsPanel extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
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="permission">
|
||||||
<div className="username-container">
|
<div className="username-container">
|
||||||
<i className="fa fa-user user-indicator"/>
|
<i className="fa fa-user user-indicator"/>
|
||||||
<input
|
<input
|
||||||
style={{color: this.getColorFor(p.user)}}
|
style={{color: this.getColorFor(p.username)}}
|
||||||
onChange={this.handleChangePermissionUser(p)}
|
onChange={this.handleChangePermissionUser(p)}
|
||||||
type="text"
|
type="text"
|
||||||
|
autoComplete="off"
|
||||||
disabled={p.owner}
|
disabled={p.owner}
|
||||||
value={p.user}
|
value={p.username}
|
||||||
className="form-control username"
|
className="form-control username"
|
||||||
placeholder={_("Username / e-mail")}
|
placeholder={_("Username")}
|
||||||
ref={(domNode) => this.lastTextbox = domNode} />
|
ref={(domNode) => this.lastTextbox = domNode} />
|
||||||
</div>
|
</div>
|
||||||
<div className="remove">
|
<div className="remove">
|
||||||
|
@ -133,7 +169,7 @@ class EditPermissionsPanel extends React.Component {
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>);
|
</form>);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="edit-permissions-panel">
|
<div className="edit-permissions-panel">
|
||||||
|
|
|
@ -69,6 +69,12 @@ SINGLE_USER_MODE = False
|
||||||
# URL to redirect to if there are no processing nodes when visiting the dashboard
|
# URL to redirect to if there are no processing nodes when visiting the dashboard
|
||||||
PROCESSING_NODES_ONBOARDING = None
|
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
|
# Enable desktop mode. In desktop mode some styling changes
|
||||||
# are applied to make the application look nicer on desktop
|
# are applied to make the application look nicer on desktop
|
||||||
# as well as disabling certain features (e.g. sharing)
|
# as well as disabling certain features (e.g. sharing)
|
||||||
|
|
Ładowanie…
Reference in New Issue