Commit 943f9ad6 authored by Leigh Stoller's avatar Leigh Stoller

Add an option to the manage profile page that allows the creator (and

project leader) to give write (editing) permission to everyone else in
the project.
parent bddf6dc4
......@@ -487,6 +487,8 @@ sub Create($$$$$$)
if (exists($argref->{'shared'}) && $argref->{'shared'});
$cquery .= ",topdog=1"
if (exists($argref->{'topdog'}) && $argref->{'topdog'});
$cquery .= ",project_write=1"
if (exists($argref->{'project_write'}) && $argref->{'project_write'});
# Create the main entry:
if (! DBQueryWarn("insert into apt_profiles set $cquery")) {
......@@ -649,7 +651,7 @@ sub UpdateMetaData($$)
# This is the only metadata we can update.
#
my %mods = ();
foreach my $key ("listed", "shared", "public", "topdog") {
foreach my $key ("listed", "shared", "public", "topdog", "project_write") {
if (exists($argref->{$key})) {
$mods{$key} = $argref->{$key};
}
......
......@@ -92,6 +92,7 @@ use libEmulab;
use libtestbed;
use User;
use Project;
use Group;
use APT_Profile;
use APT_Instance;
use APT_Aggregate;
......@@ -174,6 +175,7 @@ my %xmlfields =
"profile_listed" => ["listed", $SLOT_OPTIONAL|$SLOT_UPDATE],
"profile_public" => ["public", $SLOT_OPTIONAL|$SLOT_UPDATE],
"profile_shared" => ["shared", $SLOT_OPTIONAL|$SLOT_UPDATE],
"profile_project_write"=>["project_write", $SLOT_OPTIONAL|$SLOT_UPDATE],
"profile_topdog" => ["topdog", $SLOT_OPTIONAL|
$SLOT_UPDATE|$SLOT_ADMINONLY],
"profile_disabled" => ["disabled", $SLOT_OPTIONAL|
......@@ -617,6 +619,10 @@ sub ModifyProfile()
if (!defined($profile)) {
fatal("Could not lookup profile for update $uuid");
}
my $creator = User->Lookup($profile->creator_idx());
if (!defined($creator)) {
fatal("Could not lookup creator for $profile");
}
# This will exit if there are any errors.
VerifyXML($xmlfile, 1);
......@@ -629,8 +635,28 @@ sub ModifyProfile()
if (!defined($project)) {
UserError({"profile_pid" => "No such project exists"});
}
elsif (!$project->AccessCheck($this_user, TB_PROJECT_MAKEIMAGEID())) {
UserError({"profile_pid" => "Not enough permission in this project"});
#
# Project leader can always update profiles in the project. Other
# members can update the profile when the project_write bit has been
# set.
#
if (! ($this_user->IsAdmin() ||
$this_user->SameUser($creator) ||
$project->IsLeader($this_user) ||
$profile->project_write())) {
UserError({"profile_pid" =>
"Not enough permission to modify profile"});
}
#
# Slightly different test when changing the project_write bit.
#
if (exists($update_args{"project_write"}) &&
$update_args{"project_write"} != $profile->project_write() &&
!($this_user->IsAdmin() ||
$this_user->SameUser($creator) ||
$project->IsLeader($this_user))) {
UserError({"profile_pid" =>
"Not enough permission change project write flag"});
}
if ($profile->Lock()) {
UserError("Profile is busy, cannot lock it.");
......
#!/usr/bin/perl -wT
#
# Copyright (c) 2005-2017 University of Utah and the Flux Group.
# Copyright (c) 2005-2018 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
......@@ -32,6 +32,7 @@ use vars qw(@ISA @EXPORT);
use libdb;
use libtestbed;
use EmulabConstants;
use emutil;
use User;
use English;
......@@ -1377,6 +1378,7 @@ sub ValidGID($$)
package Group::MemberShip;
use libdb;
use libtestbed;
use EmulabConstants;
use English;
use overload ('""' => 'Stringify');
use vars qw($TRUSTSTRING_NONE $TRUSTSTRING_USER
......@@ -1455,6 +1457,16 @@ sub date_approved($) { return field($_[0], "date_approved"); }
sub group($) { return $_[0]->{'GROUP'}; }
sub user($ ) { return $_[0]->{'USER'}; }
#
# Is user trust in the group at least equal to the supplied trust
#
sub MinTrust($$)
{
my ($self, $trust) = @_;
return TBMinTrust(TBTrustConvert($self->trust()), TBTrustConvert($trust));
}
#
# Refresh a class instance by reloading from the DB.
#
......
......@@ -580,6 +580,20 @@ sub GetLeader($)
return User->Lookup($self->head_idx());
}
#
# Is the user the project leader.
#
sub IsLeader($$)
{
my ($self, $user) = @_;
# Must be a real reference.
return 0
if (! (ref($self) && ref($user)));
return $user->SameUser($self->GetLeader());
}
#
# Return project group.
#
......
......@@ -604,6 +604,7 @@ CREATE TABLE `apt_profiles` (
`no_image_versions` tinyint(1) NOT NULL default '0',
`disabled` tinyint(1) NOT NULL default '0',
`nodelete` tinyint(1) NOT NULL default '0',
`project_write` tinyint(1) NOT NULL default '0',
`locked` datetime default NULL,
`locker_pid` int(11) default '0',
`lastused` datetime default NULL,
......
......@@ -1339,6 +1339,7 @@ REPLACE INTO table_regex VALUES ('apt_profiles','public','int','redirect','defau
REPLACE INTO table_regex VALUES ('apt_profiles','listed','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','shared','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','topdog','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','project_write','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','disabled','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','nodelete','int','redirect','default:boolean',0,0,NULL);
REPLACE INTO table_regex VALUES ('apt_profiles','description','text','redirect','default:html_fulltext',0,512,NULL);
......
use strict;
use libdb;
sub DoUpdate($$$)
{
my ($dbhandle, $dbname, $version) = @_;
if (!DBSlotExists("apt_profiles", "project_write")) {
DBQueryFatal("alter table apt_profiles add " .
" `project_write` tinyint(1) NOT NULL default '0' ".
" after `nodelete`");
}
DBQueryFatal("REPLACE INTO table_regex VALUES ".
"('apt_profiles','project_write','int','redirect',".
"'default:boolean',0,0,NULL)");
return 0;
}
1;
# Local Variables:
# mode:perl
# End:
......@@ -58,7 +58,7 @@ $(function () {
// Squeeze vertical space for this field.
if (_.has(item.dataset, "compact")) {
margin = 5;
margin = 0;
}
// Column size per row,
if (_.has(item.dataset, "colsize")) {
......@@ -262,6 +262,7 @@ $(function () {
$.each(fields, function(i, field) {
formfields[field.name] = field.value;
});
console.info("Checkform", formfields);
ClearFormErrors(form);
var checkonly_callback = function(json) {
......@@ -298,6 +299,7 @@ $(function () {
$.each(fields, function(i, field) {
formfields[field.name] = field.value;
});
console.info("Submitform", formfields);
var submit_callback = function(json) {
console.info("SubmitForm", json);
sup.HideWaitWait();
......
......@@ -146,6 +146,8 @@ $(function ()
canpublish: window.CANPUBLISH,
isadmin: window.ISADMIN,
isstud: window.ISSTUD,
iscreator: window.ISCREATOR,
isleader: window.ISLEADER,
history: window.HISTORY,
activity: window.ACTIVITY,
manual: window.MANUAL,
......@@ -602,6 +604,7 @@ $(function ()
$('#profile_topdog').change(function() { ProfileModified(); });
$('#profile_disabled').change(function() { ProfileModified(); });
$('#profile_nodelete').change(function() { ProfileModified(); });
$('#profile_project_write').change(function() { ProfileModified(); });
/*
* A double click handler that will render the instructions or
......
......@@ -85,7 +85,9 @@ function Do_Create()
$errors["errors"] = "Profile has been deleted!";
goto bad;
}
if ($this_idx != $profile->creator_idx() && !ISADMIN()) {
if (! ($profile->isCreator($this_user) ||
$profile->isLeader($this_user) ||
$profile->project_write() || !ISADMIN())) {
$errors["errors"] = "Not enough permission!";
goto bad;
}
......@@ -291,12 +293,26 @@ function Do_Create()
}
fwrite($fp, "</value></attribute>\n");
}
fwrite($fp, "<attribute name='profile_shared'><value>" .
($who == "shared" ? 1 : 0) . "</value></attribute>\n");
fwrite($fp, "<attribute name='profile_public'><value>" .
($who == "public" ? 1 : 0) . "</value></attribute>\n");
if (isset($formfields["profile_project_write"])) {
fwrite($fp, "<attribute name='profile_project_write'><value>");
if ($formfields["profile_project_write"] == "checked") {
fwrite($fp, "1");
}
else {
fwrite($fp, "0");
}
fwrite($fp, "</value></attribute>\n");
}
if (ISADMIN()) {
fwrite($fp, "<attribute name='profile_topdog'><value>");
if (isset($formfields["profile_topdog"]) &&
$formfields["profile_topdog"] == "checked") {
fwrite($fp, "1");
......
......@@ -76,6 +76,8 @@ function SPITFORM($formfields, $errors)
$ispp = 0;
$isadmin = (ISADMIN() ? 1 : 0);
$isstud = (STUDLY() ? 1 : 0);
$isleader = 0;
$iscreator = 0;
$canrepo = (ISADMIN() || STUDLY() ? 1 : 0);
$multisite = ($ISCLOUD ? 1 : 0);
$cloning = 0;
......@@ -100,6 +102,8 @@ function SPITFORM($formfields, $errors)
$activity = ($profile->HasActivity() ? 1 : 0);
$ispp = ($profile->isParameterized() ? 1 : 0);
$disabled = ($profile->isDisabled() ? 1 : 0);
$isleader = ($profile->isLeader($this_user) ? 1 : 0);
$iscreator = ($profile->isCreator($this_user) ? 1 : 0);
$this_version = $profile->version();
if ($canmodify) {
$title = "Modify Profile";
......@@ -124,6 +128,7 @@ function SPITFORM($formfields, $errors)
}
$button_label = "Create";
$title = "Create Profile";
$iscreator = 1;
}
SPITHEADER(1);
......@@ -203,7 +208,9 @@ function SPITFORM($formfields, $errors)
echo " window.DISABLED= $disabled;\n";
echo " window.ISADMIN = $isadmin;\n";
echo " window.ISSTUD = $isstud;\n";
echo " window.MULTISITE = $multisite;\n";
echo " window.ISCREATOR = $iscreator;\n";
echo " window.ISLEADER = $isleader;\n";
echo " window.MULTISITE = $multisite;\n";
echo " window.HISTORY = $history;\n";
echo " window.CLONING = $cloning;\n";
echo " window.COPYING = $copying;\n";
......@@ -447,6 +454,8 @@ if (! isset($create)) {
($profile->isDisabled() ? "checked" : "");
$defaults["profile_nodelete"] =
($profile->isLocked() ? "checked" : "");
$defaults["profile_project_write"] =
($profile->project_write() ? "checked" : "");
# Warm fuzzy message.
if (isset($_SESSION["notifyupdate"])) {
......
......@@ -129,6 +129,7 @@ class Profile
function topdog() { return $this->field('topdog'); }
function disabled() { return $this->field('disabled'); }
function nodelete() { return $this->field('nodelete'); }
function project_write(){ return $this->field('project_write'); }
function repourl() { return $this->field('repourl'); }
function reponame() { return $this->field('reponame'); }
function repohash() { return $this->field('repohash'); }
......@@ -472,7 +473,8 @@ class Profile
return $this->CanInstantiate($user);
}
function CanEdit($user) {
if ($this->creator_idx() == $user->uid_idx() || ISADMIN()) {
if ($this->project_write() ||
$this->creator_idx() == $user->uid_idx() || ISADMIN()) {
return 1;
}
$project = Project::Lookup($this->pid_idx());
......@@ -503,6 +505,22 @@ class Profile
}
return 0;
}
function isLeader($user) {
$project = Project::Lookup($this->pid_idx());
if (!$project) {
return 0;
}
if ($user->uid_idx() == $project->GetLeader()->uid_idx()) {
return 1;
}
return 0;
}
function isCreator($user) {
if ($user->uid_idx() == $this->creator_idx()) {
return 1;
}
return 0;
}
function UsageInfo($user) {
$profile_id = $this->profileid();
......
......@@ -561,14 +561,15 @@
<!-- Permission checkboxes. -->
<div class='row'>
<div class='col-sm-9 col-sm-offset-3'>
<div class='col-sm-9 col-sm-offset-3' style="margin-top: 5px">
Who can instantiate your profile?
</div>
</div>
<div class='row'>
<div class='col-sm-8 col-sm-offset-4'>
<div class='format-me' data-key='profile_who'>
<div class='format-me' data-key='profile_who'
data-compact='yep'>
<% if (1) { %>
<div class='radio'>
<label>
......@@ -610,6 +611,37 @@
</div>
</div>
</div>
<% if (isadmin || iscreator || isleader) { %>
<div class='row'>
<div class='col-sm-9 col-sm-offset-3'>
<div class='checkbox format-me'
data-key='profile_project_write'
data-compact='yep'>
<input type="hidden"
name='profile_project_write' value="unchecked">
<label>
<input name='profile_project_write'
<%- formfields.profile_project_write %>
data-key='profile_project_write'
id='profile_project_write' value='checked'
type='checkbox'>Allow members of your project to
modify this profile.
<a href='#' class='btn btn-xs'
data-toggle='popover'
data-content='Normally only you or your project leader
can modify your profile. Setting this
flag allows anyone in your project to
modify this profile, which is helpful
when collaborating with other members
of your project.'>
<span style='margin-bottom: 4px;'
class='glyphicon glyphicon-question-sign'></span>
</a>
</label>
</div>
</div>
</div>
<% } %>
<% if (isadmin) { %>
<div class='row'>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment