Completed
Push — master ( b78927...2357ce )
by Damian
20:56
created

SecureEditableFileField::getIsSecure()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php
2
3
/**
4
 * Provides additional file security for uploaded files when the securefiles module is installed
5
 *
6
 * {@see EditableFileField}
7
 */
8
class SecureEditableFileField extends DataExtension
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
9
{
10
11
    /**
12
     * Path to secure files location under assets
13
     *
14
     * @config
15
     * @var type
16
     */
17
    private static $secure_folder_name = 'SecureUploads';
0 ignored issues
show
Unused Code introduced by
The property $secure_folder_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
18
19
    /**
20
     * Disable file security if a user-defined mechanism is in place
21
     *
22
     * @config
23
     * @var bool
24
     */
25
    private static $disable_security = false;
0 ignored issues
show
Unused Code introduced by
The property $disable_security is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
26
27
    /*
28
     * Check if file security is enabled
29
     *
30
     * @return bool
31
     */
32 18
    public function getIsSecurityEnabled()
33
    {
34
        // Skip if requested
35 18
        if ($this->owner->config()->disable_security) {
36 1
            return false;
37
        }
38
39
        // Check for necessary security module
40 18
        if (!class_exists('SecureFileExtension')) {
41
            trigger_error('SecureEditableFileField requires secureassets module', E_USER_WARNING);
42
            return false;
43
        }
44
45 18
        return true;
46
    }
47
48 1
    public function requireDefaultRecords()
49
    {
50
        // Skip if disabled
51 1
        if (!$this->getIsSecurityEnabled()) {
52
            return;
53
        }
54
55
        // Update all instances of editablefilefield which do NOT have a secure folder assigned
56 1
        foreach (EditableFileField::get() as $fileField) {
57
            // Skip if secured
58 1
            if ($fileField->getIsSecure()) {
59
                continue;
60
            }
61
62
            // Force this field to secure itself on write
63 1
            $fileField->write(false, false, true);
64 1
            DB::alteration_message(
65 1
                "Restricting editable file field \"{$fileField->Title}\" to secure folder",
66
                "changed"
67 1
            );
68 1
        }
69 1
    }
70
71
    /**
72
     * Secure this field before saving
73
     */
74 18
    public function onBeforeWrite()
75 17
    {
76 18
        $this->makeSecure();
77 18
    }
78
79
    /**
80
     * Ensure this field is secured, but does not write changes to the database
81
     */
82 18
    public function makeSecure()
83
    {
84
        // Skip if disabled or already secure
85 18
        if (!$this->getIsSecurityEnabled() || $this->owner->getIsSecure()) {
86 17
            return;
87
        }
88
89
        // Ensure folder exists
90 18
        $folder = $this->owner->Folder();
91 18
        if (!$folder || !$folder->exists()) {
92
            // Create new folder in default location
93 17
            $folder = Folder::find_or_make($this->owner->config()->secure_folder_name);
94 17
            $this->owner->FolderID = $folder->ID;
95 18
        } elseif ($this->isFolderSecured($folder)) {
96
            // If folder exists and is secure stop
97
            return;
98
        }
99
100
        // Make secure
101 18
        $folder->CanViewType = 'OnlyTheseUsers';
102 18
        $folder->ViewerGroups()->add($this->findAdminGroup());
103 18
        $folder->write();
104 18
    }
105
106
    /**
107
     * Find target group to record
108
     *
109
     * @return Group
110
     */
111 18
    protected function findAdminGroup()
112
    {
113 18
        singleton('Group')->requireDefaultRecords();
114 18
        return Permission::get_groups_by_permission('ADMIN')->First();
115
    }
116
117
    /**
118
     * Determine if the field is secure
119
     *
120
     * @return bool
121
     */
122 18
    public function getIsSecure()
123
    {
124 18
        return $this->isFolderSecured($this->owner->Folder());
125
    }
126
127
    /**
128
     * Check if a Folder object is secure
129
     *
130
     * @param Folder $folder
131
     * @return boolean
132
     */
133 18
    protected function isFolderSecured($folder)
134
    {
135 18
        if (! ($folder instanceof Folder) || !$folder->exists()) {
136 18
            return false;
137
        }
138
139 18
        switch ($folder->CanViewType) {
140 18
            case 'OnlyTheseUsers':
141 18
                return true;
142 3
            case 'Inherit':
143 3
                $parent = $folder->Parent();
144 3
                return $parent && $parent->exists() && $this->isFolderSecured($parent);
0 ignored issues
show
Compatibility introduced by
$parent of type object<File> is not a sub-type of object<Folder>. It seems like you assume a child class of the class File to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
145
            case 'Anyone':
146
            case 'LoggedInUsers':
147
            default:
148
                return false;
149
        }
150
    }
151
}
152