Added permissions for the new functions.

This commit is contained in:
Jan Böhmer 2019-09-13 17:13:58 +02:00
parent d2bae3a4f2
commit da14ee942d
14 changed files with 213 additions and 64 deletions

View file

@ -12,7 +12,7 @@ groups:
perms: # Here comes a list with all Permission names (they have a perm_[name] coloumn in DB)
# Part related permissions
# Part related permissions
parts: # e.g. this maps to perms_parts in User/Group database
group: "parts"
@ -29,10 +29,10 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
label: "perm.create"
bit: 4
alsoSet: ['read', 'edit']
move:
label: "perm.part.move"
bit: 6
alsoSet: 'read'
#move:
# label: "perm.part.move"
# bit: 6
# alsoSet: 'read'
delete:
label: "perm.delete"
bit: 8
@ -80,15 +80,15 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
bit: 2
alsoSet: 'read'
parts_category:
<<: *PART_ATTRIBUTE
label: "perm.part.category"
parts_description:
<<: *PART_ATTRIBUTE
label: "perm.part.description"
parts_instock:
<<: *PART_ATTRIBUTE
label: "perm.part.instock"
parts_mininstock:
parts_minamount:
<<: *PART_ATTRIBUTE
label: "perm.part.mininstock"
@ -100,24 +100,61 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
<<: *PART_ATTRIBUTE
label: "perm.part.comment"
parts_storelocation:
<<: *PART_ATTRIBUTE
label: "perm.part.storelocation"
parts_manufacturer:
<<: *PART_ATTRIBUTE
label: "perm.part.manufacturer"
parts_orderdetails:
parts_mpn:
<<: *PART_ATTRIBUTE
label: "perm.part.mpn"
parts_status:
<<: *PART_ATTRIBUTE
label: "perm.part.status"
parts_tags:
<<: *PART_ATTRIBUTE
label: "perm.part.tags"
parts_unit:
<<: *PART_ATTRIBUTE
label: "perm.part.unit"
parts_mass:
<<: *PART_ATTRIBUTE
label: "perm.part.mass"
parts_orderdetails: &PART_MULTI_ATTRIBUTE
label: "perm.part.orderdetails"
group: "parts"
operations:
read:
label: "perm.read"
bit: 0
edit:
label: "perm.edit"
bit: 2
alsoSet: 'read'
create:
label: "perm.create"
bit: 4
alsoSet: ['read', 'edit']
delete:
label: "perm.delete"
bit: 6
alsoSet: ['read']
parts_prices:
<<: *PART_ATTRIBUTE
<<: *PART_MULTI_ATTRIBUTE
label: "perm.part.prices"
parts_lots:
<<: *PART_MULTI_ATTRIBUTE
label: "perm.part.lots"
parts_attachments:
<<: *PART_ATTRIBUTE
<<: *PART_MULTI_ATTRIBUTE
label: "perm.part.attachments"
parts_order:
@ -150,7 +187,7 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
label: "perm.list_parts"
bit: 10
show_users:
label: "perm.list_parts"
label: "perm.show_users"
bit: 12
footprints:
@ -177,6 +214,14 @@ perms: # Here comes a list with all Permission names (they have a perm_[name] co
<<: *PART_CONTAINING
label: "perm.part.attachment_types"
currencies:
<<: *PART_CONTAINING
label: "perm.currencies"
measurement_units:
<<: *PART_CONTAINING
label: "perm.measurement_units"
tools:
label: "perm.part.tools"
operations:

View file

@ -130,7 +130,7 @@ class Part extends AttachmentContainingDBElement
* @var Orderdetail[]
* @ORM\OneToMany(targetEntity="App\Entity\PriceInformations\Orderdetail", mappedBy="part", cascade={"persist", "remove"}, orphanRemoval=true)
* @Assert\Valid()
* @ColumnSecurity(prefix="orderdetails", type="object")
* @ColumnSecurity(prefix="orderdetails", type="collection")
*/
protected $orderdetails;

View file

@ -129,6 +129,11 @@ class PermissionsEmbed
*/
protected $parts_name = 0;
/** @var int
* @ORM\Column(type="smallint")
*/
protected $parts_category = 0;
/**
* @var int
* @ORM\Column(type="smallint")
@ -139,13 +144,7 @@ class PermissionsEmbed
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_instock = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_mininstock = 0;
protected $parts_minamount = 0;
/**
* @var int
@ -157,7 +156,24 @@ class PermissionsEmbed
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_storelocation = 0;
protected $parts_lots = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_tags = 0;
/** @var int
* @ORM\Column(type="smallint")
*/
protected $parts_unit = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_mass = 0;
/**
* @var int
@ -165,6 +181,18 @@ class PermissionsEmbed
*/
protected $parts_manufacturer = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_status = 0;
/**
* @var int
* @ORM\Column(type="smallint")
*/
protected $parts_mpn = 0;
/**
* @var int
* @ORM\Column(type="smallint")
@ -243,6 +271,17 @@ class PermissionsEmbed
*/
protected $attachment_types = 0;
/** @var int
* @ORM\Column(type="integer")
*/
protected $currencies = 0;
/**
* @var int
* @ORM\Column(type="integer")
*/
protected $measurement_units = 0;
/**
* @var int
* @ORM\Column(type="integer")
@ -276,7 +315,7 @@ class PermissionsEmbed
public function getBitValue(string $permission_name, int $bit_n): int
{
if(!$this->isValidPermissionName($permission_name)) {
throw new \InvalidArgumentException('No permission with the given name is existing!');
throw new \InvalidArgumentException(sprintf('No permission with the name "%s" is existing!', $permission_name));
}
$perm_int = $this->$permission_name;

View file

@ -166,6 +166,12 @@ class User extends NamedDBElement implements UserInterface, HasPermissionsInterf
*/
protected $group;
/**
* @var array
* @ORM\Column(type="json")
*/
protected $settings = [];
/** @var PermissionsEmbed
* @ORM\Embedded(class="PermissionsEmbed", columnPrefix="perms_")
* @ValidPermission()
@ -173,7 +179,8 @@ class User extends NamedDBElement implements UserInterface, HasPermissionsInterf
protected $permissions;
/**
* @ORM\Column(type="string", name="config_currency")
* @ORM\ManyToOne(targetEntity="App\Entity\PriceInformations\Currency", fetch="EAGER")
* @ORM\JoinColumn(name="currency_id", referencedColumnName="id")
*/
protected $currency = "";

View file

@ -109,13 +109,13 @@ class PartBaseType extends AbstractType
'attr' => ['min' => 0, 'placeholder' => $this->trans->trans('part.editmininstock.placeholder')],
'label' => $this->trans->trans('part.edit.mininstock'),
'measurement_unit' => $part->getPartUnit(),
'disabled' => !$this->security->isGranted('mininstock.edit', $part),
'disabled' => !$this->security->isGranted('minamount.edit', $part),
])
->add('category', StructuralEntityType::class, [
'class' => Category::class,
'label' => $this->trans->trans('part.edit.category'),
'disable_not_selectable' => true,
'disabled' => !$this->security->isGranted('move', $part),
'disabled' => !$this->security->isGranted('category.edit', $part),
])
->add('footprint', StructuralEntityType::class, [
'class' => Footprint::class,
@ -129,7 +129,7 @@ class PartBaseType extends AbstractType
'label' => $this->trans->trans('part.edit.tags'),
'empty_data' => "",
'attr' => ['data-role' => 'tagsinput'],
'disabled' => !$this->security->isGranted('edit', $part)
'disabled' => !$this->security->isGranted('tags.edit', $part)
]);
//Manufacturer section
@ -144,41 +144,45 @@ class PartBaseType extends AbstractType
'required' => false,
'empty_data' => '',
'label' => $this->trans->trans('part.edit.manufacturer_url.label'),
'disabled' => !$this->security->isGranted('manufacturer.edit', $part),
'disabled' => !$this->security->isGranted('mpn.edit', $part),
])
->add('manufacturer_product_number', TextType::class, [
'required' => false,
'empty_data' => '',
'label' => $this->trans->trans('part.edit.mpn'),
'disabled' => !$this->security->isGranted('manufacturer.edit', $part)])
'disabled' => !$this->security->isGranted('mpn.edit', $part)])
->add('manufacturing_status', ChoiceType::class, [
'label' => $this->trans->trans('part.edit.manufacturing_status'),
'choices' => $status_choices,
'required' => false,
'disabled' => !$this->security->isGranted('manufacturer.edit', $part)
'disabled' => !$this->security->isGranted('status.edit', $part)
]);
//Advanced section
$builder->add('needsReview', CheckboxType::class, [
'label_attr' => ['class' => 'checkbox-custom'],
'required' => false,
'label' => $this->trans->trans('part.edit.needs_review')
'label' => $this->trans->trans('part.edit.needs_review'),
'disabled' => !$this->security->isGranted('edit', $part)
])
->add('favorite', CheckboxType::class, [
'label_attr' => ['class' => 'checkbox-custom'],
'required' => false,
'label' => $this->trans->trans('part.edit.is_favorite')
'label' => $this->trans->trans('part.edit.is_favorite'),
'disabled' => !$this->security->isGranted('change_favorite', $part)
])
->add('mass', SIUnitType::class, [
'unit' => 'g',
'label' => $this->trans->trans('part.edit.mass'),
'required' => false
'required' => false,
'disabled' => !$this->security->isGranted('mass.edit', $part)
])
->add('partUnit', StructuralEntityType::class, [
'class' => MeasurementUnit::class,
'required' => false,
'disable_not_selectable' => true,
'label' => $this->trans->trans('part.edit.partUnit')
'label' => $this->trans->trans('part.edit.partUnit'),
'disabled' => !$this->security->isGranted('unit.edit', $part)
]);
@ -193,10 +197,12 @@ class PartBaseType extends AbstractType
//Part Lots section
$builder->add('partLots', CollectionType::class, [
'entry_type' => PartLotType::class,
'allow_add' => true, 'allow_delete' => true,
'allow_add' => $this->security->isGranted('lots.create', $part),
'allow_delete' => $this->security->isGranted('lots.delete', $part),
'label' => false,
'entry_options' => [
'measurement_unit' => $part->getPartUnit()
'measurement_unit' => $part->getPartUnit(),
'disabled' => !$this->security->isGranted('lots.edit', $part),
],
'by_reference' => false
]);
@ -204,16 +210,19 @@ class PartBaseType extends AbstractType
//Attachment section
$builder->add('attachments', CollectionType::class, [
'entry_type' => AttachmentFormType::class,
'allow_add' => true, 'allow_delete' => true,
'allow_add' => $this->security->isGranted('attachments.create', $part),
'allow_delete' => $this->security->isGranted('attachments.delete', $part),
'label' => false,
'entry_options' => [
'data_class' => PartAttachment::class
'data_class' => PartAttachment::class,
'disabled' => !$this->security->isGranted('attachments.edit', $part),
],
'by_reference' => false
]);
$builder->add('master_picture_attachment', EntityType::class, [
'required' => false,
'disabled' => !$this->security->isGranted('attachments.edit', $part),
'label' => $this->trans->trans('part.edit.master_attachment'),
'class' => PartAttachment::class,
'attr' => ['class' => 'selectpicker'],
@ -238,12 +247,14 @@ class PartBaseType extends AbstractType
//Orderdetails section
$builder->add('orderdetails', CollectionType::class, [
'entry_type' => OrderdetailType::class,
'allow_add' => true, 'allow_delete' => true,
'allow_add' => $this->security->isGranted('orderdetails.create', $part),
'allow_delete' => $this->security->isGranted('orderdetails.delete', $part),
'label' => false,
'by_reference' => false,
'prototype_data' => new Orderdetail(),
'entry_options' => [
'measurement_unit' => $part->getPartUnit()
'measurement_unit' => $part->getPartUnit(),
'disabled' => !$this->security->isGranted('attachments.edit', $part),
]
]);

View file

@ -51,16 +51,19 @@ use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Security\Core\Security;
use Symfony\Contracts\Translation\TranslatorInterface;
use function GuzzleHttp\Promise\queue;
class PartLotType extends AbstractType
{
protected $trans;
protected $security;
public function __construct(TranslatorInterface $trans)
public function __construct(TranslatorInterface $trans, Security $security)
{
$this->trans = $trans;
$this->security = $security;
}
public function buildForm(FormBuilderInterface $builder, array $options)

View file

@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20190913141126 extends AbstractMigration
{
public function getDescription() : string
{
return '';
}
public function up(Schema $schema) : void
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE `groups` ADD perms_parts_category SMALLINT NOT NULL, ADD perms_parts_minamount SMALLINT NOT NULL, ADD perms_parts_lots SMALLINT NOT NULL, ADD perms_parts_tags SMALLINT NOT NULL, ADD perms_parts_unit SMALLINT NOT NULL, ADD perms_parts_mass SMALLINT NOT NULL, ADD perms_parts_status SMALLINT NOT NULL, ADD perms_parts_mpn SMALLINT NOT NULL, ADD perms_currencies INT NOT NULL, ADD perms_measurement_units INT NOT NULL, DROP perms_parts_instock, DROP perms_parts_mininstock, DROP perms_parts_storelocation');
$this->addSql('ALTER TABLE users ADD currency_id INT DEFAULT NULL, ADD settings LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', ADD perms_parts_category SMALLINT NOT NULL, ADD perms_parts_minamount SMALLINT NOT NULL, ADD perms_parts_lots SMALLINT NOT NULL, ADD perms_parts_tags SMALLINT NOT NULL, ADD perms_parts_unit SMALLINT NOT NULL, ADD perms_parts_mass SMALLINT NOT NULL, ADD perms_parts_status SMALLINT NOT NULL, ADD perms_parts_mpn SMALLINT NOT NULL, ADD perms_currencies INT NOT NULL, ADD perms_measurement_units INT NOT NULL, DROP config_currency, DROP perms_parts_instock, DROP perms_parts_mininstock, DROP perms_parts_storelocation');
$this->addSql('ALTER TABLE users ADD CONSTRAINT FK_1483A5E938248176 FOREIGN KEY (currency_id) REFERENCES currencies (id)');
$this->addSql('CREATE INDEX IDX_1483A5E938248176 ON users (currency_id)');
}
public function down(Schema $schema) : void
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE `groups` ADD perms_parts_instock SMALLINT NOT NULL, ADD perms_parts_mininstock SMALLINT NOT NULL, ADD perms_parts_storelocation SMALLINT NOT NULL, DROP perms_parts_category, DROP perms_parts_minamount, DROP perms_parts_lots, DROP perms_parts_tags, DROP perms_parts_unit, DROP perms_parts_mass, DROP perms_parts_status, DROP perms_parts_mpn, DROP perms_currencies, DROP perms_measurement_units');
$this->addSql('ALTER TABLE `users` DROP FOREIGN KEY FK_1483A5E938248176');
$this->addSql('DROP INDEX IDX_1483A5E938248176 ON `users`');
$this->addSql('ALTER TABLE `users` ADD config_currency VARCHAR(255) NOT NULL COLLATE utf8_general_ci, ADD perms_parts_instock SMALLINT NOT NULL, ADD perms_parts_mininstock SMALLINT NOT NULL, ADD perms_parts_storelocation SMALLINT NOT NULL, DROP currency_id, DROP settings, DROP perms_parts_category, DROP perms_parts_minamount, DROP perms_parts_lots, DROP perms_parts_tags, DROP perms_parts_unit, DROP perms_parts_mass, DROP perms_parts_status, DROP perms_parts_mpn, DROP perms_currencies, DROP perms_measurement_units');
}
}

View file

@ -30,6 +30,7 @@
namespace App\Security\Annotations;
use Doctrine\Common\Annotations\Annotation;
use Doctrine\Common\Collections\ArrayCollection;
use \InvalidArgumentException;
/**
@ -65,7 +66,7 @@ class ColumnSecurity
/**
* @var string The name of the property. This is used to determine the default placeholder.
* @Annotation\Enum({"integer", "string", "object", "boolean", "datetime"})
* @Annotation\Enum({"integer", "string", "object", "boolean", "datetime", "collection"})
*/
public $type = 'string';
@ -97,6 +98,8 @@ class ColumnSecurity
return '???';
case 'object':
return null;
case 'collection':
return new ArrayCollection();
case 'boolean':
return false;
case 'datetime':

View file

@ -26,10 +26,10 @@ class PartVoter extends ExtendedVoter
if (false !== strpos($attribute, '.')) {
[$perm, $op] = explode('.', $attribute);
return in_array($op, $this->resolver->listOperationsForPermission('parts_'.$perm), false);
return $this->resolver->isValidOperation('parts_' . $perm, $op);
}
return in_array($attribute, $this->resolver->listOperationsForPermission('parts'), false);
return $this->resolver->isValidOperation('parts', $attribute);
}
return false;

View file

@ -86,11 +86,9 @@ class StructureVoter extends ExtendedVoter
case Supplier::class:
return 'suppliers';
case Currency::class:
//TODO: Implement own permission
return 'suppliers';
return 'currencies';
case MeasurementUnit::class:
//TODO: Implement own permission
return 'suppliers';
return 'measurement_units';
}
//When the class is not supported by this class return null
return null;

View file

@ -1,5 +1,5 @@
{% set delete_btn %}
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_attachment_entry(this);">
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_attachment_entry(this);" {% if not is_granted('attachments.delete', part) %}disabled{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i>
{% trans %}attachment.delete{% endtrans %}
</button>
@ -7,7 +7,7 @@
{{ form_row(form.master_picture_attachment) }}
<table class="table table-striped table-sm" id="attachments_table" data-prototype="{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}">
<table class="table table-striped table-sm" id="attachments_table" data-prototype="{% if form.attachments.vars.prototype is defined %}{{ form_widget(form.attachments.vars.prototype)|e('html_attr') }}{% endif %}">
<tbody>
{% for attachment in form.attachments %}
<tr>
@ -54,7 +54,7 @@
</tbody>
</table>
<button type="button" class="btn btn-success" onclick="create_attachment_entry(this)">
<button type="button" class="btn btn-success" onclick="create_attachment_entry(this)" {% if not is_granted('attachments.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}attachment.create{% endtrans %}
</button>

View file

@ -1,12 +1,13 @@
{% set delete_btn %}
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_lot_entry(this);">
<button type="button" class="btn btn-danger lot_btn_delete" onclick="delete_lot_entry(this);"
{% if not is_granted('lots.delete', part) %}disabled{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i>
{% trans %}part_lot.delete{% endtrans %}
</button>
{% endset %}
<table class="table table-striped table-sm" id="lots_table" data-prototype="{{ form_widget(form.partLots.vars.prototype)|e('html_attr') }}">
<table class="table table-striped table-sm" id="lots_table" data-prototype="{% if form.partLots.vars.prototype is defined %}{{ form_widget(form.partLots.vars.prototype)|e('html_attr') }}{% endif %}">
<tbody>
{% for lot in form.partLots %}
<tr>
@ -21,7 +22,8 @@
</tbody>
</table>
<button type="button" class="btn btn-success" onclick="create_lot_entry(this)">
<button type="button" class="btn btn-success" onclick="create_lot_entry(this)"
{% if not is_granted('lots.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}part_lot.create{% endtrans %}
</button>

View file

@ -1,14 +1,14 @@
{% form_theme form with ['Parts/edit/edit_form_styles.html.twig', "bootstrap_4_layout.html.twig"] %}
<table class="table table-striped table-sm" id="orderdetails_table" data-prototype="{{ form_widget(form.orderdetails.vars.prototype)|e('html_attr') }}">
<table class="table table-striped table-sm" id="orderdetails_table" data-prototype="{% if form.orderdetails.vars.prototype is defined %}{{ form_widget(form.orderdetails.vars.prototype)|e('html_attr') }}{% endif %}">
<tbody>
{% for detail in form.orderdetails %}
{{ form_widget(detail) }}
{{ form_widget(detail, {'disable_delete' : not is_granted('orderdetails.delete', part)}) }}
{% endfor %}
</tbody>
</table>
<button type="button" class="btn btn-success" onclick="create_orderdetail_entry(this)">
<button type="button" class="btn btn-success" onclick="create_orderdetail_entry(this)" {% if not is_granted('orderdetails.create', part) %}disabled{% endif %}>
<i class="fas fa-plus-square fa-fw"></i>
{% trans %}orderdetail.create{% endtrans %}
</button>

View file

@ -53,7 +53,7 @@
</button>
</td>
<td>
<button type="button" class="btn btn-danger order_btn_delete" onclick="delete_orderdetail_entry(this);" title="{% trans %}orderdetail.delete{% endtrans %}">
<button type="button" class="btn btn-danger order_btn_delete" onclick="delete_orderdetail_entry(this);" title="{% trans %}orderdetail.delete{% endtrans %}" {% if disable_delete %}disabled{% endif %}>
<i class="fas fa-trash-alt fa-fw"></i>
</button>
{{ form_errors(form) }}