diff --git a/.github/update-composer-json.php b/.github/update-composer-json.php new file mode 100644 index 000000000..2144c54fb --- /dev/null +++ b/.github/update-composer-json.php @@ -0,0 +1,137 @@ + 'package', // The type is at the root level of the repository entry + 'package' => [ + 'name' => $packageName, + 'version' => $packageVersionRange, + 'type' => 'library', // Default type, you can adjust this if needed + 'dist' => [ + 'url' => $repositoryUrl, + 'type' => $distType, // Use the dynamically derived dist type + 'reference' => $packageHash // Adding the hash (reference) if available + ] + ] + ]; + // Track pinned package + $pinnedPackages[] = $packageName; + } +} + +// Write the updated composer.json back to the file +if (file_put_contents($composerJsonPath, json_encode($composerJson, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL)) { + echo "composer.json has been updated with the versions from composer.lock.\n"; + if (count($pinnedPackages) > 0) { + echo "The following packages were pinned to repositories:\n"; + foreach ($pinnedPackages as $packageName) { + echo " - $packageName\n"; + } + } +} else { + echo "Failed to update composer.json.\n"; + exit(1); +} diff --git a/.github/workflows/auto-assign-pr.yml b/.github/workflows/auto-assign-pr.yml new file mode 100644 index 000000000..fecb812bc --- /dev/null +++ b/.github/workflows/auto-assign-pr.yml @@ -0,0 +1,24 @@ +name: Auto-Assign PR + +on: + pull_request_target: + types: [opened] + +permissions: + issues: write + +jobs: + assign: + runs-on: ubuntu-latest + steps: + - name: Assign PR to repo owner + uses: actions/github-script@v8 + with: + github-token: ${{ secrets.GH_ADMIN_TOKEN }} + script: | + await github.rest.issues.addAssignees({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: context.issue.number, + assignees: ["proditis"] + }); diff --git a/.markdownlint.yaml b/.markdownlint.yaml index 7ace1f4be..6a7d02cde 100644 --- a/.markdownlint.yaml +++ b/.markdownlint.yaml @@ -3,4 +3,6 @@ MD012: false MD013: false # Disable required empty line after heading MD022: false -MD033: false \ No newline at end of file +MD033: false +MD040: false +MD043: false \ No newline at end of file diff --git a/ansible/Dockerfiles/sanitycheck/variables.yml b/ansible/Dockerfiles/sanitycheck/variables.yml index 9766edd51..1762de0cb 100644 --- a/ansible/Dockerfiles/sanitycheck/variables.yml +++ b/ansible/Dockerfiles/sanitycheck/variables.yml @@ -12,6 +12,7 @@ writeup_allowed: 0 headshot_spin: 0 instance_allowed: 0 TargetOndemand: false +dynamic_treasures: 0 container: name: "{{hostname}}" hostname: "{{fqdn}}" diff --git a/ansible/files/login.conf.d/memcached b/ansible/files/login.conf.d/memcached new file mode 100644 index 000000000..6c6704d35 --- /dev/null +++ b/ansible/files/login.conf.d/memcached @@ -0,0 +1,5 @@ +memcached:\ + :datasize=2048M:\ + :openfiles-cur=6144:\ + :openfiles-max=20480:\ + :tc=daemon: diff --git a/ansible/files/login.conf.d/mysqld b/ansible/files/login.conf.d/mysqld new file mode 100644 index 000000000..ec90ada63 --- /dev/null +++ b/ansible/files/login.conf.d/mysqld @@ -0,0 +1,5 @@ +mysqld:\ + :datasize=8192M:\ + :openfiles-cur=6144:\ + :openfiles-max=20480:\ + :tc=daemon: diff --git a/ansible/files/login.conf.d/supervisord b/ansible/files/login.conf.d/supervisord new file mode 100644 index 000000000..a91739203 --- /dev/null +++ b/ansible/files/login.conf.d/supervisord @@ -0,0 +1,8 @@ +supervisord:\ + :ignorenologin:\ + :datasize=4096M:\ + :maxproc=infinity:\ + :openfiles-max=10024:\ + :openfiles-cur=2560:\ + :stacksize-cur=18M:\ + :tc=default: diff --git a/ansible/files/login.conf.d/watchdoger b/ansible/files/login.conf.d/watchdoger new file mode 100644 index 000000000..23fab3b31 --- /dev/null +++ b/ansible/files/login.conf.d/watchdoger @@ -0,0 +1,8 @@ +watchdoger:\ + :ignorenologin:\ + :datasize=512M:\ + :maxproc=infinity:\ + :openfiles-max=4096:\ + :openfiles-cur=1024:\ + :stacksize-cur=8M:\ + :tc=default: diff --git a/ansible/files/pui.service.conf b/ansible/files/pui.service.conf index fb1fa8ea0..9dd62ce24 100644 --- a/ansible/files/pui.service.conf +++ b/ansible/files/pui.service.conf @@ -1,6 +1,8 @@ # Allow users to connect to port 80/443 pass in quick on egress inet proto tcp from {, } to (egress:0) port 8888 rdr-to 127.0.0.1 +pass in quick on interconnect inet proto tcp from (interconnect:network) to (interconnect:0) port 8888 rdr-to 127.0.0.1 + pass quick from label "administrators" pass quick inet proto tcp from to (egress:0) port { 80 , 443 } label "www-moderators" @@ -8,9 +10,7 @@ pass quick inet proto tcp from to (egress:0) port { 80 , 443 } labe # FOR DT OPERATIONS pass in quick inet proto tcp from to port 80 rdr-to 127.0.0.1 port 8080 label "maintenance" pass in quick inet proto tcp from to port 443 rdr-to 127.0.0.1 port 8443 label "maintenance" -block in quick on egress inet proto tcp from to (egress:0) port 8888 pass in on egress inet proto tcp from to port { 80, 443 } label "www-normal" pass in on egress inet proto tcp to port { 80, 443 } label "www-normal" -pass in quick on egress inet proto tcp to (egress:0) port 8888 rdr-to 127.0.0.1 diff --git a/ansible/inventories/servers/host_vars/registry.yml b/ansible/inventories/servers/host_vars/registry.yml index 87ad4d3cc..37fa85f1e 100644 --- a/ansible/inventories/servers/host_vars/registry.yml +++ b/ansible/inventories/servers/host_vars/registry.yml @@ -5,7 +5,7 @@ registry_storage: "/storage" registry_bind_ip: "0.0.0.0" registry_bind_port: "5000" registry_targets_if: if0 -registry_targets_cidr: 10.0.100.0/24 +registry_targets_cidr: 10.0.0.100/24 backups: - { tgz: "/altroot/root.tgz", src: '/root' } - { tgz: "/altroot/etc.tgz", src: '/etc' } diff --git a/ansible/maintenance/authorized_keys.yml b/ansible/maintenance/authorized_keys.yml new file mode 100644 index 000000000..cda89202b --- /dev/null +++ b/ansible/maintenance/authorized_keys.yml @@ -0,0 +1,33 @@ +--- +- name: Update authorized SSH keys for a user + hosts: all + become: true + + vars: + target_user: root + + pre_tasks: + - name: Ensure ssh_keys_source is provided + ansible.builtin.fail: + msg: "You must provide ssh_keys_source (file path or URL)" + when: ssh_keys_source is not defined + + - name: Load SSH public keys from file or URL + ansible.builtin.set_fact: + ssh_public_keys: >- + {{ + lookup( + ssh_keys_source is match('^https?://') + | ternary('url', 'file'), + ssh_keys_source + ) + }} + + tasks: + - name: Update authorized_keys for user + ansible.posix.authorized_key: + user: "{{ target_user }}" + key: "{{ ssh_public_keys }}" + manage_dir: true + state: present + # exclusive: true # uncomment to replace all existing keys diff --git a/ansible/maintenance/sync-github.yml b/ansible/maintenance/sync-github.yml new file mode 100644 index 000000000..e413658f2 --- /dev/null +++ b/ansible/maintenance/sync-github.yml @@ -0,0 +1,32 @@ +#!/usr/bin/env ansible-playbook +--- +- name: Update deployed application from Git + hosts: all + tasks: + - name: Ensure repository is up to date + git: + repo: "{{ GITHUB_REPO }}" + dest: "{{ REPO }}" + version: "{{ GITHUB_REPO_BRANCH }}" + force: yes + accept_hostkey: yes + update: yes + notify: restart applications + register: git_result + + - name: Clean untracked files (preserving paths) + shell: git clean -fd {{ '-n' if ansible_check_mode else '' }} {% for p in PRESERVE_PATHS %}-e {{ p }} {% endfor %} + args: + chdir: "{{ REPO }}" + register: clean_result + changed_when: clean_result.stdout != "" + when: git_result.changed and PRESERVE_PATHS is defined + notify: restart applications + + handlers: + - name: restart applications + when: APP_SERVICES is defined + service: + name: "{{ item }}" + state: restarted + loop: "{{ APP_SERVICES }}" diff --git a/ansible/runonce/db.yml b/ansible/runonce/db.yml index 3409ab90d..2e087e5d2 100755 --- a/ansible/runonce/db.yml +++ b/ansible/runonce/db.yml @@ -411,6 +411,27 @@ group: wheel mode: '0555' + - name: copy watchdoger script + copy: + src: ../../contrib/watchdoger.py + dest: /usr/local/bin/watchdoger + owner: root + group: wheel + mode: '0555' + + - name: Install watchdoger.ini for supervisord + copy: + dest: /etc/supervisord.d/watchdoger.ini + content: | + [program:watchdoger] + user = root + command = su -fm -c watchdoger -s /usr/local/bin/python3 root /usr/local/bin/watchdoger --file_path /tmp/event_finished --url {{ wsserver.url | default("http://10.7.0.200:8888/broadcast") }} --token {{ wsserver.token | default("server123token") }} + autorestart = false + startretries = 0 + stdout_logfile=/var/log/watchdoger.log + stdout_logfile_maxbytes=0 + redirect_stderr=true + - name: Setting up sysctl.conf sysctl: name: "{{ item.key }}" @@ -473,6 +494,16 @@ - { name: "events checker", minute: "*/1", job: "-ns /usr/local/sbin/mysql-events-checker" } - { name: "daily database backups", minute: "0",hour: "23", job: "-ns /usr/local/sbin/database_backup" } + - name: copy login.conf.d login classes + copy: + src: "../files/login.conf.d/{{item}}" + dest: "/etc/login.conf.d/{{item}}" + with_items: + - memcached + - mysqld + - supervisord + - watchdoger + - name: Execute fw_update command: fw_update -a diff --git a/ansible/runonce/docker-registry.yml b/ansible/runonce/docker-registry.yml index 547330003..49b150e46 100755 --- a/ansible/runonce/docker-registry.yml +++ b/ansible/runonce/docker-registry.yml @@ -137,7 +137,7 @@ content: "{{item.content|default(omit)}}" with_items: - { dest: "/etc/pf.conf", src: "../files/pf.conf"} - - { dest: "/etc/service.pf.conf", content: "anchor \"dynamic\"\npass quick inet proto tcp from to port {{registry_bind_port}} label \"service_clients\"\n"} + - { dest: "/etc/service.pf.conf", content: "pass quick from label \"administrators\"\nanchor \"dynamic\"\npass quick inet proto tcp from to port {{registry_bind_port}} label \"service_clients\"\n"} - { dest: "/etc/service_clients.conf", content: "{{registry_targets_cidr}}\n"} - name: Dump administrators PF table (if exists) diff --git a/ansible/runonce/docker-servers.yml b/ansible/runonce/docker-servers.yml index af2024e10..1b9d3e404 100755 --- a/ansible/runonce/docker-servers.yml +++ b/ansible/runonce/docker-servers.yml @@ -1,40 +1,17 @@ #!/usr/bin/env ansible-playbook --- -- name: Configure docker servers for echoCTF +- name: Bootstrap docker servers for echoCTF hosts: all gather_facts: true become: true become_method: su + remote_user: sysadmin tasks: + - name: Set timezone to UTC timezone: name: UTC - - name: Kill any running pm2 - shell: pm2 kill - no_log: true - ignore_errors: true - - - name: Ensure docker services are stopped - command: service docker stop - no_log: true - ignore_errors: true - - - name: Ensure existing docker service overrides are removed - ansible.builtin.file: - path: "{{item}}" - state: absent - with_items: - - /etc/systemd/system/docker.service.d/dockerd-service-override.conf - - /etc/systemd/system/docker.service.d/override.conf - - /etc/systemd/system/pm2-root.service.d/pm2-root-service-override.conf - - /etc/systemd/system/pm2-root.service.d/override.conf - - - name: Remove any existing /etc/docker/daemon.json - ansible.builtin.file: - path: /etc/docker/daemon.json - state: absent - - name: Set hostname based on host_var hostname: name: "{{fqdn}}" @@ -61,29 +38,6 @@ pkg: "{{pre_apt}}" when: pre_apt is defined and pre_apt|length > 0 - - name: Add apt keys - when: aptKeys is defined - apt_key: - url: "{{item.key}}" - state: "{{item.state}}" - with_items: "{{aptKeys}}" - - - name: Add apt repositories - when: aptRepos is defined - apt_repository: - repo: "{{item.repo}}" - state: "{{item.state}}" - with_items: "{{aptRepos}}" - - - name: Update package cache - apt: - update_cache: yes - - - name: Update all packages to the latest version - no_log: "{{DEBUG|default(true)}}" - apt: - upgrade: dist - - name: Adding defined users (optional) when: users is defined user: @@ -109,16 +63,73 @@ key: "https://github.com/{{item}}.keys" with_items: "{{sshkeys}}" - - name: Make sure sysadmin has sudo access - lineinfile: - path: /etc/sudoers.d/90_sysadmin - line: 'sysadmin ALL=(ALL) NOPASSWD:ALL' - create: yes - - name: Ensure /home/sysadmin is owned by sysadmin user (recursive) shell: chown -R sysadmin /home/sysadmin when: users is defined + - name: Allow user to have passwordless sudo + copy: + dest: "/etc/sudoers.d/90_sysadmin" + content: "sysadmin ALL=(ALL) NOPASSWD:ALL\n" + owner: root + group: root + mode: '0440' + +- name: Configure docker servers for echoCTF + hosts: all + gather_facts: true + become: false + remote_user: root + tasks: + + - name: Kill any running pm2 + shell: pm2 kill + no_log: true + ignore_errors: true + + - name: Ensure docker services are stopped + command: service docker stop + no_log: true + ignore_errors: true + + - name: Ensure existing docker service overrides are removed + ansible.builtin.file: + path: "{{item}}" + state: absent + with_items: + - /etc/systemd/system/docker.service.d/dockerd-service-override.conf + - /etc/systemd/system/docker.service.d/override.conf + - /etc/systemd/system/pm2-root.service.d/pm2-root-service-override.conf + - /etc/systemd/system/pm2-root.service.d/override.conf + + - name: Remove any existing /etc/docker/daemon.json + ansible.builtin.file: + path: /etc/docker/daemon.json + state: absent + + - name: Add apt keys + when: aptKeys is defined + apt_key: + url: "{{item.key}}" + state: "{{item.state}}" + with_items: "{{aptKeys}}" + + - name: Add apt repositories + when: aptRepos is defined + apt_repository: + repo: "{{item.repo}}" + state: "{{item.state}}" + with_items: "{{aptRepos}}" + + - name: Update package cache + apt: + update_cache: yes + + - name: Update all packages to the latest version + no_log: "{{DEBUG|default(true)}}" + apt: + upgrade: dist + - name: Install post install packages apt: state: latest diff --git a/ansible/runonce/mui.yml b/ansible/runonce/mui.yml index b3c247965..4f27b56e5 100755 --- a/ansible/runonce/mui.yml +++ b/ansible/runonce/mui.yml @@ -272,7 +272,7 @@ dest: "{{item.dest}}" with_items: - { src: '{{playbook_dir}}/../templates/httpd.conf.j2', dest: '/etc/httpd.conf', domain: '{{hostname}}' } - - { src: '{{playbook_dir}}/../templates/acme-client.conf.j2', dest: '/etc/acme-client.conf', domain: '{{hostname}}', challenge_dir: "/home/moderatortUI/acme/.well-known/acme-challenge/" } + - { src: '{{playbook_dir}}/../templates/acme-client.conf.j2', dest: '/etc/acme-client.conf', domain: '{{hostname}}', challenge_dir: "/home/moderatorUI/acme/.well-known/acme-challenge/" } - name: Generate pf tables files command: "{{item.cmd}}" @@ -425,8 +425,8 @@ - name: configure folders and perms command: "{{item}}" with_items: - - "mkdir -p /home/moderatorUI/{{domain_name}}/backend/web/assets" - - "chown -R moderatorUI /home/moderatorUI/{{domain_name}}/backend/web/assets" + - "install -d -o moderatorUI /home/moderatorUI/{{domain_name}}/backend/web/assets" + - "install -d -o moderatorUI /home/moderatorUI/{{domain_name}}/backend/web/identifcationFiles" - "mkdir -p /var/log/cron" - "ln -sf /home/moderatorUI/{{domain_name}}/backend/yii /usr/local/bin/backend" diff --git a/ansible/runonce/pui.yml b/ansible/runonce/pui.yml index 9a1ed2bf4..cb439619a 100755 --- a/ansible/runonce/pui.yml +++ b/ansible/runonce/pui.yml @@ -270,6 +270,7 @@ - { src: '{{playbook_dir}}/../files/pui.service.conf', dest: '/etc/service.pf.conf' } - { src: '{{playbook_dir}}/../../contrib/unbound.conf', dest: '/var/unbound/etc/unbound.conf' } - { src: '{{playbook_dir}}/../../contrib/downtime.html', dest: '/home/participantUI/htdocs/index.html' } + - { src: '{{playbook_dir}}/../../contrib/downtime.html', dest: '/home/participantUI/{{domain_name}}/frontend/web/dt.html' } - name: "Process templates" template: @@ -417,7 +418,7 @@ src: ../templates/dt.conf.j2 dest: /etc/nginx/dt.conf with_items: - - { user: 'www', domain: '{{domain_name}}', root: "/htdocs" } + - { user: 'participantUI', domain: '{{domain_name}}', root: "/htdocs" } tags: - nginx @@ -460,12 +461,13 @@ - { section: PHP, option: "expose_php", value: "Off" } - { section: PHP, option: "log_errors_max_len", value: 4096 } - { section: PHP, option: "html_errors", value: "Off" } - - { section: PHP, option: "max_execution_time", value: "60" } + - { section: PHP, option: "max_execution_time", value: "180" } - { section: PHP, option: "max_input_time", value: "120" } - { section: PHP, option: "memory_limit", value: "256M" } - { section: PHP, option: "error_reporting", value: "E_NONE" } - { section: PHP, option: "post_max_size", value: "180M" } - { section: PHP, option: "upload_max_filesize", value: "120M" } + - { section: PHP, option: "allow_url_fopen", value: "On"} - { section: Session, option: "session.save_handler", value: "memcached"} - { section: Session, option: "session.save_path", value: "{{db_ip}}:11211"} - { section: Session, option: "session.gc_maxlifetime", value: "43200" } @@ -487,13 +489,10 @@ - name: configure folders and permissions command: "{{item}}" with_items: - - mkdir -p /home/participantUI/{{domain_name}}/frontend/web/assets - - mkdir -p /home/participantUI/{{domain_name}}/frontend/web/identificationFiles - - mkdir -p /home/participantUI/{{domain_name}}/frontend/web/images/avatars/team + - install -d -o participantUI /home/participantUI/{{domain_name}}/frontend/web/assets + - install -d -o participantUI /home/participantUI/{{domain_name}}/frontend/web/identificationFiles + - install -d -o participantUI /home/participantUI/{{domain_name}}/frontend/web/images/avatars/team - mkdir -p /var/log/cron - - chown -R participantUI /home/participantUI/{{domain_name}}/frontend/web/assets - - chown -R participantUI /home/participantUI/{{domain_name}}/frontend/web/images/avatars/ - - chown -R participantUI /home/participantUI/{{domain_name}}/frontend/web/identificationFiles - ln -sf /home/participantUI/{{domain_name}}/frontend/yii /usr/local/bin/frontend - name: configure participant rc.d @@ -635,10 +634,12 @@ redirect_stderr=true - name: "Update frontend/config/params.php" - replace: - path: "/home/participantUI/{{domain_name}}/frontend/config/params.php" - regexp: 'ws://localhost:8888/ws' - replace: 'wss://{{domain_name}}/ws' + ansible.builtin.lineinfile: + path: "/home/participantUI/{{ domain_name }}/frontend/config/params.php" + search_string: 'ws://localhost:8888/ws' + line: 'wss://{{ domain_name }}/ws' + insertafter: null + insertbefore: null - name: copy nstables script copy: diff --git a/ansible/runonce/vpngw.yml b/ansible/runonce/vpngw.yml index 495bcef33..d2c3902ac 100755 --- a/ansible/runonce/vpngw.yml +++ b/ansible/runonce/vpngw.yml @@ -252,6 +252,19 @@ - name: Activate install php modules shell: "cp /etc/php-{{versions.PHP}}.sample/*.ini /etc/php-{{versions.PHP}}/" + - name: Fixing /etc/php.ini + ini_file: dest=/etc/php-{{versions.PHP}}.ini section={{item.section}} option={{item.option}} value={{item.value}} mode=0644 owner=root group=wheel + with_items: + - { section: PHP, option: "expose_php", value: "Off" } + - { section: PHP, option: "log_errors_max_len", value: 4096 } + - { section: PHP, option: "html_errors", value: "Off" } + - { section: PHP, option: "max_execution_time", value: "60" } + - { section: PHP, option: "max_input_time", value: "120" } + - { section: PHP, option: "memory_limit", value: "256M" } + - { section: PHP, option: "error_reporting", value: "E_NONE" } + - { section: PHP, option: "post_max_size", value: "180M" } + - { section: PHP, option: "upload_max_filesize", value: "120M" } + - { section: PHP, option: "allow_url_fopen", value: "On"} - name: Update my.cnf ini_file: @@ -686,6 +699,28 @@ stdout_logfile_maxbytes=0 redirect_stderr=true + - name: copy watchdog-action script + copy: + src: ../../contrib/watchdog-action.py + dest: /usr/local/bin/watchdog-action + owner: root + group: wheel + mode: '0555' + + - name: copy event_shutdown script + copy: + src: ../../contrib/event_shutdown.sh + dest: /usr/local/sbin/event_shutdown + owner: root + group: wheel + mode: '0555' + + - name: Add watchdog-action to rc.local + lineinfile: + path: "/etc/rc.local" + line: "nohup /usr/local/bin/watchdog-action --file_path /tmp/event_finished --action /usr/local/sbin/event_shutdown >/tmp/watchdog.log 2>&1 &" + insertafter: EOF + - name: create user for openvpn up/down scripts mysql_user: name: "openvpn" diff --git a/ansible/templates/dt.conf.j2 b/ansible/templates/dt.conf.j2 index 17f7650b4..5a3699bdc 100644 --- a/ansible/templates/dt.conf.j2 +++ b/ansible/templates/dt.conf.j2 @@ -43,7 +43,15 @@ http { ssl_prefer_server_ciphers on; ssl_dhparam /etc/ssl/private/dhparam.pem; - return 503; + # ACME challenge must bypass maintenance + location ^~ /.well-known/acme-challenge/ { + root /acme; + try_files $uri =404; + } + + location / { + return 503; + } error_page 503 @maintenance; location @maintenance { add_header Strict-Transport-Security "max-age=63072000; includeSubDomains"; diff --git a/ansible/templates/nginx.conf.j2 b/ansible/templates/nginx.conf.j2 index f2584dcc6..5bd6c159c 100644 --- a/ansible/templates/nginx.conf.j2 +++ b/ansible/templates/nginx.conf.j2 @@ -22,6 +22,8 @@ http { fastcgi_temp_path /cache/fastcgi_temp 1 2; proxy_temp_path /cache/proxy_temp 1 2; + client_max_body_size 32M; + limit_req_zone $binary_remote_addr zone=Api:1m rate=10r/s; limit_req_zone $binary_remote_addr zone=ResendVerificationEmail:1m rate=10r/s; limit_req_zone $binary_remote_addr zone=RequestPasswordReset:1m rate=10r/s; @@ -35,6 +37,8 @@ http { #tcp_nopush on; keepalive_timeout 65; + fastcgi_send_timeout 180s; + fastcgi_read_timeout 180s; gzip on; gzip_static on; gzip_proxied expired no-cache no-store private auth; @@ -206,27 +210,13 @@ http { } # disable direct access to dt.html - location = /dt.html { - return 404; - } - location = /429.html { - return 404; - } - location = /500.html { - return 404; - } - location = /501.html { - return 404; - } - location = /504.html { - return 404; - } - location = /505.html { - return 404; - } - location ~ /\.ht { - return 404; - } + location = /dt.html { return 404; } + location = /429.html { return 404; } + location = /500.html { return 404; } + location = /501.html { return 404; } + location = /504.html { return 404; } + location = /505.html { return 404; } + location ~ /\.ht { return 404; } location /contrib/ { autoindex on; @@ -252,6 +242,15 @@ http { set $yii_bootstrap "/index.php"; {% if "mui" not in inventory_hostname %} + {% if mui_ext_ip is defined %} + location ^~ /identificationFiles/ { + allow {{ mui_ext_ip }}; + deny all; + + try_files $uri $uri/ =404; + } + {% endif %} + {% if enable_rate_limit is defined %} # Rate limit /api to 10 requests/sec # This is way too loose location /api { @@ -325,6 +324,7 @@ http { index index.php; try_files $uri $uri/ /index.php$is_args$args; } + {% endif %} {% endif %} location / { index index.html index.php; @@ -336,18 +336,18 @@ http { return 404; } {% endif %} - location /ws { - proxy_pass http://127.0.0.1:8888/ws; - - # WebSocket-specific headers - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; - - # Optional: increase timeout for long-lived connections - proxy_read_timeout 86400s; - proxy_send_timeout 86400s; - } + location /ws { + proxy_pass http://127.0.0.1:8888/ws; + + # WebSocket-specific headers + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "Upgrade"; + + # Optional: increase timeout for long-lived connections + proxy_read_timeout 86400s; + proxy_send_timeout 86400s; + } {% if captcha is defined %} # Check for requests to /profile/ location ~ ^/profile/\d+$ { @@ -372,10 +372,10 @@ http { # let yii catch the calls to non-existing PHP files set $fsn /$yii_bootstrap; set $yiiargs r=$request_uri; - if (-f $document_root$fastcgi_script_name){ - set $fsn $fastcgi_script_name; + #if (-f $document_root$fastcgi_script_name){ + # set $fsn $fastcgi_script_name; set $yiiargs $query_string; - } + #} fastcgi_pass {{item.fpm}}; include fastcgi_params; @@ -399,10 +399,10 @@ http { # let yii catch the calls to non-existing PHP files set $fsn /$yii_bootstrap; set $yiiargs r=$request_uri; - if (-f $document_root$fastcgi_script_name){ - set $fsn $fastcgi_script_name; - set $yiiargs $query_string; - } + #if (-f $document_root$fastcgi_script_name){ + # set $fsn $fastcgi_script_name; + # set $yiiargs $query_string; + #} fastcgi_pass {{item.fpm}}; include fastcgi_params; diff --git a/backend/commands/CronController.php b/backend/commands/CronController.php index ec16a5bc0..dda45d3bf 100644 --- a/backend/commands/CronController.php +++ b/backend/commands/CronController.php @@ -15,6 +15,7 @@ use app\modules\infrastructure\models\TargetInstanceAudit; use app\modules\infrastructure\models\NetworkTargetSchedule as NTS; use app\modules\gameplay\models\NetworkTarget; +use app\components\helpers\ArrayHelperExtended; /** * @method docker_connect() @@ -159,11 +160,7 @@ public function actionInstancePf($before = 60) case SELF::ACTION_START: case SELF::ACTION_RESTART: if (($val->team_allowed === true || \Yii::$app->sys->team_visible_instances === true) && $val->player->teamPlayer !== null && $val->player->teamPlayer->approved === 1) { - foreach ($val->player->teamPlayer->team->approvedMembers as $teamPlayer) { - if ((int)$teamPlayer->player->last->vpn_local_address !== 0) { - $ips[] = long2ip($teamPlayer->player->last->vpn_local_address); - } - } + $ips = $val->player->teamPlayer->team->approvedMemberIPs; } else if ((int)$val->player->last->vpn_local_address !== 0) { $ips[] = long2ip($val->player->last->vpn_local_address); } @@ -200,15 +197,9 @@ public function actionInstancePfTables($dopf = false) } $team_visible_instances = \Yii::$app->sys->team_visible_instances; if ($val->team_allowed == true || $team_visible_instances === true) { - // get team members if ($val->player->teamPlayer) { $team = $val->player->teamPlayer->team; - foreach ($team->approvedMembers as $member) { - // get IP's of connected players - if (($pIP = $member->player->last->vpn_local_address) !== null) { - $IPs[] = long2ip($pIP); - } - } + $IPs = ArrayHelperExtended::mergeUnique($IPs, $team->approvedMemberIPs); } } @@ -224,8 +215,16 @@ public function actionInstancePfTables($dopf = false) /** * Process player private instances + * + * This command updates the last updated_at field for active instances + * and processes instances that are pending action (eg. reboot, destroy, powerup) + * and updates the PF according to currently online players. + * + * @param bool $pfonly Perform PF operations and skip instance docker actions (default: false) + * @param int $expired_ago Filter instances based that haven't been updated X seconds ago (default: 2400 seconds = 40 minutes) + * @return int Exit code */ - public function actionInstances($pfonly = false) + public function actionInstances(bool $pfonly = false, int $expired_ago = 2400) { if (file_exists("/tmp/cron-instances.lock")) { echo date("Y-m-d H:i:s ") . "Instances: /tmp/cron-instances.lock exists, skipping execution\n"; @@ -233,21 +232,13 @@ public function actionInstances($pfonly = false) } touch("/tmp/cron-instances.lock"); $action = SELF::ACTION_EXPIRED; - // Get powered instances - $t = TargetInstance::find()->active(); - foreach ($t->all() as $instance) { - if ($instance->player->last->vpn_local_address !== null && $pfonly === false) { - printf("Updating heartbeat [%d: %s for %d: %s]\n", $instance->target_id, $instance->target->name, $instance->player_id, $instance->player->username); - $instance->touch('updated_at'); - } - } - - $t = TargetInstance::find()->pending_action(40); + $t = TargetInstance::find()->pending_action($expired_ago); foreach ($t->all() as $val) { try { $ips = []; $dc = new DockerContainer($val->target); - $dc->timeout = ($val->server->timeout ? $val->server->timeout : 2000); + $dc->timeout = ($val->server->timeout ? $val->server->timeout : 10000); + if ($val->target->targetVolumes !== null) $dc->targetVolumes = $val->target->targetVolumes; @@ -279,17 +270,18 @@ public function actionInstances($pfonly = false) $dc->server = $val->server->connstr; $dc->net = $val->server->network; - if ($val->ip == null) { - echo date("Y-m-d H:i:s ") . "Starting"; - $action = SELF::ACTION_START; + if ($val->reboot === 2) { + echo date("Y-m-d H:i:s ") . "Destroying"; + $action = SELF::ACTION_DESTROY; } else if ($val->reboot === 1) { echo date("Y-m-d H:i:s ") . "Restarting"; $action = SELF::ACTION_RESTART; - } else if ($val->reboot === 2) { - echo date("Y-m-d H:i:s ") . "Destroying"; - $action = SELF::ACTION_DESTROY; + } else if ($val->ip == null && $val->reboot == 0) { + echo date("Y-m-d H:i:s ") . "Starting"; + $action = SELF::ACTION_START; } else { echo date("Y-m-d H:i:s ") . "Expiring"; + $action = SELF::ACTION_EXPIRED; } printf(" %s for %s (%s) at %s\n", $val->target->name, $val->player->username, $dc->name, $val->server->name); switch ($action) { @@ -298,17 +290,14 @@ public function actionInstances($pfonly = false) if ($pfonly === false) { try { $dc->destroy(); - } catch (\Exception $e) { + } catch (\Throwable $e) { } $dc->pull(); + usleep(100); $dc->spin(); } if (($val->team_allowed === true || \Yii::$app->sys->team_visible_instances === true) && $val->player->teamPlayer && $val->player->teamPlayer->approved === 1) { - foreach ($val->player->teamPlayer->team->approvedMembers as $teamPlayer) { - if ((int)$teamPlayer->player->last->vpn_local_address !== 0) { - $ips[] = long2ip($teamPlayer->player->last->vpn_local_address); - } - } + $ips = $val->player->teamPlayer->team->approvedMemberIPs; } else if ((int)$val->player->last->vpn_local_address !== 0) { $ips[] = long2ip($val->player->last->vpn_local_address); } @@ -317,8 +306,10 @@ public function actionInstances($pfonly = false) $val->ipoctet = $dc->container->getNetworkSettings()->getNetworks()->{$val->server->network}->getIPAddress(); Pf::add_table_ip($dc->name, $val->ipoctet, true); $val->reboot = 0; - if ($pfonly === false) + if ($pfonly === false) { + $val->updated_at = new \yii\db\Expression('NOW()'); $val->save(); + } break; case SELF::ACTION_EXPIRED: @@ -337,14 +328,32 @@ public function actionInstances($pfonly = false) default: printf("Error: Unknown action\n"); } - } catch (\Exception $e) { + } catch (\Throwable $e) { if (method_exists($e, 'getErrorResponse')) - echo "Instances:", $e->getErrorResponse()->getMessage(), "\n"; + echo "Instances: ", $e->getErrorResponse()->getMessage(), "\n"; else - echo "Instances:", $e->getMessage(), "\n"; + echo "Instances: ", $e->getMessage(), "\n"; + if (getenv('DEBUG', true) !== false) + \Yii::error($e); } + unset($dc); + usleep(200); + $publisher = new \app\services\ServerPublisher(\Yii::$app->params['serverPublisher']); + $publisher->publish($val->player_id, 'target', ['id' => $val->target_id]); } // end foreach $this->actionInstancePfTables(true); + + if ($pfonly === false) { + try { + $t = TargetInstance::find()->active()->withApprovedMemberHeartbeat()->last_updated(round($expired_ago / 2)); + foreach ($t->all() as $instance) { + printf("Updating heartbeat [%d: %s for %d: %s]\n", $instance->target_id, $instance->target->name, $instance->player_id, $instance->player->username); + $instance->touch('updated_at'); + } + } catch (\Throwable $e) { + echo "Instrances: Error while touching updated_at. ", $e->getMessage(); + } + } @unlink("/tmp/cron-instances.lock"); } diff --git a/backend/commands/PlayerController.php b/backend/commands/PlayerController.php index 586d94869..f01f531df 100644 --- a/backend/commands/PlayerController.php +++ b/backend/commands/PlayerController.php @@ -182,7 +182,7 @@ public function actionMail($active = false, $email = false, $status = 9, $approv /* Register Users and generate OpenVPN keys and settings */ - public function actionRegister($username, $email, $fullname, $password = false, $player_type = "offense", $active = false, $academic = false, $team_name = false, $approved = 0) + public function actionRegister($username, $email, $fullname, $password = false, $player_type = "offense", bool $active = true, int $status = 9, $academic = false, $team_name = false, $approved = 0) { echo "Registering: ", $email, "\n"; $trans = Yii::$app->db->beginTransaction(); @@ -196,7 +196,7 @@ public function actionRegister($username, $email, $fullname, $password = false, $unique = Player::findOne(['username' => $username]); } $player->username = $username; - echo 'Autogenerated username: ',$player->username,"\n"; + echo 'Autogenerated username: ', $player->username, "\n"; } else $player->username = trim(str_replace(array("\xc2\xa0", "\r\n", "\r"), "", $username)); @@ -210,16 +210,16 @@ public function actionRegister($username, $email, $fullname, $password = false, $player->password = Yii::$app->security->generatePasswordHash($password); $player->active = intval($active); - $player->status = 10; + $player->status = $status; $player->auth_key = Yii::$app->security->generateRandomString(); if (!$player->saveWithSsl()) { - if (!$player->active && intval(\Yii::$app->sys->disable_mailer)==0) { - $player->generateEmailVerificationToken(); - $player->status = 9; - } - throw new ConsoleException('Failed to save player:' . $player->username, "\n"); + throw new ConsoleException('Failed to save player:' . $player->username . "\n"); + } + + if ((!$player->active || $player->status === 9) && intval(\Yii::$app->sys->disable_mailer) == 0) { + $player->generateEmailVerificationToken(); } $player->createTeam($team_name, $approved); @@ -366,23 +366,23 @@ public function actionCheckDupips($skip_uids = false) } } - public function actionFailValidation($delete = false,$extended=false,$csv=false) + public function actionFailValidation($delete = false, $extended = false, $csv = false) { $allRecords = Player::find()->where(['status' => 10])->all(); foreach ($allRecords as $p) { - if($extended!==false) + if ($extended !== false) $p->scenario = 'extendedValidator'; else $p->scenario = 'validator'; if (!$p->validate()) { - echo boolval($csv)===true ? $p->id.",".$p->username : Table::widget([ - 'headers' => ['ID: ' . $p->id.' '.$p->username, 'Description'], + echo boolval($csv) === true ? $p->id . "," . $p->username : Table::widget([ + 'headers' => ['ID: ' . $p->id . ' ' . $p->username, 'Description'], 'rows' => $this->getErrorRows($p), ]); if (boolval($delete) !== false && $p->delete()) { - echo boolval($csv)===true ? ",deleted" : $p->id." Deleted\n"; + echo boolval($csv) === true ? ",deleted" : $p->id . " Deleted\n"; } - echo boolval($csv)===true ? "\n" : ""; + echo boolval($csv) === true ? "\n" : ""; } } } @@ -470,8 +470,7 @@ public function actionFetchIdentification($filter = 'inactive', $scheme = 'https foreach ($players->all() as $player) { $baseDir = \Yii::getAlias('@app/web/identificationFiles/'); echo "processing ", $player->username; - if(!$player->metadata) - { + if (!$player->metadata) { echo " no metadata\n"; continue; } @@ -486,13 +485,10 @@ public function actionFetchIdentification($filter = 'inactive', $scheme = 'https curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); - if($status==200) - { + if ($status == 200) { echo " => grabbing ", $baseDir . $player->metadata->identificationFile, "\n"; file_put_contents($baseDir . $player->metadata->identificationFile, fopen("$scheme://" . Yii::$app->sys->offense_domain . '/identificationFiles/' . $player->metadata->identificationFile, 'r')); - } - else - { + } else { echo ", remote identification file not found\n"; } } diff --git a/backend/commands/TesterController.php b/backend/commands/TesterController.php index 8f7e288e9..77f0c8bba 100644 --- a/backend/commands/TesterController.php +++ b/backend/commands/TesterController.php @@ -23,8 +23,8 @@ public function actionIndex() echo Table::widget([ 'headers' => ['Action', 'Usage', 'Description'], 'rows' => [ - ['Action' => 'tester/mail', 'Usage' => 'tester/mail email@example.com', 'Description' => 'Send a test mail with the current settings'], -// ['Action' => 'docker', 'Usage' => 'tester/docker nginx:latest', 'Description' => 'Test the a given image to make sure it can be deployed on the configured docker servers of the platform.'], + ['Action' => 'tester/mail', 'Usage' => 'tester/mail email@example.com', 'Description' => 'Send a test mail with the current settings'], + ['Action' => 'tester/ws-notify', 'Usage' => 'tester/ws-notify player', 'Description' => 'Send a test websocket notification to the given player by id'], ], ]); } @@ -72,4 +72,17 @@ public function actionMail($to) $this->stderr("❌ Error: " . $e->getMessage() . "\n"); } } + + public function actionWsNotify($player) + { + $player=\app\modules\frontend\models\Player::findOne($player); + $type = "info"; + $title="title"; + $body="body"; + $cc = true; + $archive = true; + $apiOnly = false; + $player->notify($type, $title, $body, $cc, $archive, $apiOnly = false); + } + } diff --git a/backend/commands/VpnController.php b/backend/commands/VpnController.php index 0fa8fea47..ed837702a 100644 --- a/backend/commands/VpnController.php +++ b/backend/commands/VpnController.php @@ -120,8 +120,10 @@ public function actionLoad($filepath) $ovpn->conf = file_get_contents($filepath); $ovpn->name = $file; $ovpn->server = gethostbyaddr(gethostbyname(gethostname())); - if (preg_match('/status (.*)/', $conf, $matches) && count($matches) > 1) { - $ovpn->status_log = trim($matches[1]); + if (preg_match('/^status\s+(.+)/', $conf, $matches) && count($matches) > 1) { + $parts = explode(' ', trim($matches[0])); + if(isset($parts[1])) + $ovpn->status_log = trim($parts[1]); } if (preg_match('/management (.*) (.*) (.*)/', $conf, $matches) && count($matches) > 1) { $ovpn->management_ip_octet = $matches[1]; diff --git a/backend/components/OpenVPN.php b/backend/components/OpenVPN.php index a31faf6cb..945de6157 100644 --- a/backend/components/OpenVPN.php +++ b/backend/components/OpenVPN.php @@ -81,7 +81,7 @@ static public function parseStatus(string $location) { if(!file_exists($location)) { - throw new yii\base\UserException("Status file does not exist"); + throw new \yii\base\UserException("Status file does not exist"); } $statusLines=explode("\n",file_get_contents($location)); if(count($statusLines)==0) diff --git a/backend/components/helpers/ArrayHelperExtended.php b/backend/components/helpers/ArrayHelperExtended.php new file mode 100644 index 000000000..b170f8b48 --- /dev/null +++ b/backend/components/helpers/ArrayHelperExtended.php @@ -0,0 +1,27 @@ +db->createCommand($this->DROP_SQL)->execute(); + } + + public function down() + { + $this->db->createCommand($this->DROP_SQL)->execute(); + } +} \ No newline at end of file diff --git a/backend/modules/activity/controllers/NotificationController.php b/backend/modules/activity/controllers/NotificationController.php index 601d6288a..c555cc366 100644 --- a/backend/modules/activity/controllers/NotificationController.php +++ b/backend/modules/activity/controllers/NotificationController.php @@ -21,7 +21,18 @@ class NotificationController extends \app\components\BaseController */ public function behaviors() { - return ArrayHelper::merge(parent::behaviors(), []); + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['create', 'delete', 'rotate'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); } /** diff --git a/backend/modules/content/controllers/CreditsController.php b/backend/modules/content/controllers/CreditsController.php index 091a1084b..80badcd2b 100644 --- a/backend/modules/content/controllers/CreditsController.php +++ b/backend/modules/content/controllers/CreditsController.php @@ -17,107 +17,115 @@ class CreditsController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } - /** - * Lists all Credits models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new CreditsSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Credits models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new CreditsSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Credits model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Credits model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Credits(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single Credits model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Credits model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Credits(); - /** - * Updates an existing Credits model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Credits model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Credits model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the Credits model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Credits the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Credits::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Credits model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Credits model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Credits the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Credits::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/content/controllers/DefaultController.php b/backend/modules/content/controllers/DefaultController.php index b59cbc9c9..b36cc0caa 100644 --- a/backend/modules/content/controllers/DefaultController.php +++ b/backend/modules/content/controllers/DefaultController.php @@ -15,10 +15,21 @@ class DefaultController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['css-override', 'js-override','menu-items','frontpage-scenario','defense-scenario', 'offense-scenario', 'footer-logos'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } /** * Renders the index view for the module diff --git a/backend/modules/content/controllers/EmailTemplateController.php b/backend/modules/content/controllers/EmailTemplateController.php index 607cd2004..ad3e3f88b 100644 --- a/backend/modules/content/controllers/EmailTemplateController.php +++ b/backend/modules/content/controllers/EmailTemplateController.php @@ -20,7 +20,18 @@ class EmailTemplateController extends \app\components\BaseController */ public function behaviors() { - return ArrayHelper::merge(parent::behaviors(), []); + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete','adhoc-mail'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); } /** diff --git a/backend/modules/content/controllers/FaqController.php b/backend/modules/content/controllers/FaqController.php index f02680889..7ca9f053a 100644 --- a/backend/modules/content/controllers/FaqController.php +++ b/backend/modules/content/controllers/FaqController.php @@ -17,107 +17,115 @@ class FaqController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } - /** - * Lists all Faq models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new FaqSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Faq models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new FaqSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Faq model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Faq model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Faq(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single Faq model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Faq model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Faq(); - /** - * Updates an existing Faq model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Faq model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Faq model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the Faq model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Faq the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Faq::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Faq model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Faq model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Faq the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Faq::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/content/controllers/InstructionController.php b/backend/modules/content/controllers/InstructionController.php index 4f0168db7..576ef5c32 100644 --- a/backend/modules/content/controllers/InstructionController.php +++ b/backend/modules/content/controllers/InstructionController.php @@ -17,107 +17,115 @@ class InstructionController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } - /** - * Lists all Instruction models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new InstructionSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Instruction models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new InstructionSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Instruction model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Instruction model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Instruction(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single Instruction model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Instruction model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Instruction(); - /** - * Updates an existing Instruction model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Instruction model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Instruction model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the Instruction model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Instruction the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Instruction::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException('The requested page does not exist.'); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Instruction model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Instruction model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Instruction the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Instruction::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException('The requested page does not exist.'); + } } diff --git a/backend/modules/content/controllers/LayoutOverrideController.php b/backend/modules/content/controllers/LayoutOverrideController.php index 1d8fe4859..3044c40fa 100644 --- a/backend/modules/content/controllers/LayoutOverrideController.php +++ b/backend/modules/content/controllers/LayoutOverrideController.php @@ -15,107 +15,118 @@ */ class LayoutOverrideController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } + + /** + * Lists all LayoutOverride models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new LayoutOverrideSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single LayoutOverride model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new LayoutOverride model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new LayoutOverride(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Lists all LayoutOverride models. - * @return mixed - */ - public function actionIndex() - { - $searchModel = new LayoutOverrideSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing LayoutOverride model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Displays a single LayoutOverride model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing LayoutOverride model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the LayoutOverride model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return LayoutOverride the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = LayoutOverride::findOne($id)) !== null) { + return $model; } - /** - * Creates a new LayoutOverride model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model = new LayoutOverride(); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } - - /** - * Updates an existing LayoutOverride model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); - } - - /** - * Deletes an existing LayoutOverride model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); - } - - /** - * Finds the LayoutOverride model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return LayoutOverride the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = LayoutOverride::findOne($id)) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); - } + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/content/controllers/NewsController.php b/backend/modules/content/controllers/NewsController.php index 5c8d3bfbc..fa7b3bc22 100644 --- a/backend/modules/content/controllers/NewsController.php +++ b/backend/modules/content/controllers/NewsController.php @@ -17,130 +17,139 @@ class NewsController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[ - 'verbs' => [ - 'class' => VerbFilter::class, - 'actions' => [ - 'discord' => ['POST'], - ], - ], - ]); - } + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + 'verbs' => [ + 'class' => VerbFilter::class, + 'actions' => [ + 'discord' => ['POST'], + ], + ], + ], parent::behaviors()); + } - /** - * Lists all News models. - * @return mixed - */ - public function actionIndex() - { - $searchModel = new NewsSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all News models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new NewsSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single News model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new News model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model = new News(); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single News model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new News model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new News(); - /** - * Updates an existing News model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing News model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing News model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Posts an existing News model to discord webhook - * Upon submission (successful or not), the browser will be redirected to the 'index' page and display an appropriate messages. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDiscord($id) - { - try { - $result=$this->findModel($id)->toDiscord(); - Yii::$app->session->setFlash('success',Yii::t('app','News item got send to the discord news webhook')); - } - catch (\Exception $e) { - Yii::$app->session->setFlash('error',Yii::t('app','Failed to send news item to discord webhook')); - } - return $this->redirect(['index']); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing News model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Posts an existing News model to discord webhook + * Upon submission (successful or not), the browser will be redirected to the 'index' page and display an appropriate messages. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDiscord($id) + { + try { + $result = $this->findModel($id)->toDiscord(); + Yii::$app->session->setFlash('success', Yii::t('app', 'News item got send to the discord news webhook')); + } catch (\Exception $e) { + Yii::$app->session->setFlash('error', Yii::t('app', 'Failed to send news item to discord webhook')); } + return $this->redirect(['index']); + } - /** - * Finds the News model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return News the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = News::findOne($id)) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + /** + * Finds the News model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return News the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = News::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/content/controllers/ObjectiveController.php b/backend/modules/content/controllers/ObjectiveController.php index f3fba39c1..497c2c463 100644 --- a/backend/modules/content/controllers/ObjectiveController.php +++ b/backend/modules/content/controllers/ObjectiveController.php @@ -17,107 +17,115 @@ class ObjectiveController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } - /** - * Lists all Objective models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new ObjectiveSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Objective models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new ObjectiveSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Objective model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Objective model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Objective(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single Objective model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Objective model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Objective(); - /** - * Updates an existing Objective model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Objective model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Objective model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the Objective model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Objective the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Objective::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException('The requested page does not exist.'); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Objective model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Objective model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Objective the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Objective::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException('The requested page does not exist.'); + } } diff --git a/backend/modules/content/controllers/PagesController.php b/backend/modules/content/controllers/PagesController.php index 4367b5736..e18c88e4a 100644 --- a/backend/modules/content/controllers/PagesController.php +++ b/backend/modules/content/controllers/PagesController.php @@ -8,112 +8,124 @@ use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; use yii\helpers\ArrayHelper; + /** * PagesController implements the CRUD actions for Pages model. */ class PagesController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } - /** - * Lists all Pages models. - * @return mixed - */ - public function actionIndex() - { - $searchModel = new PagesSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Pages models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new PagesSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Pages model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Pages model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model = new Pages(); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single Pages model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Pages model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Pages(); - /** - * Updates an existing Pages model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Pages model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Pages model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the Pages model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Pages the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = Pages::findOne($id)) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Pages model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Pages model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Pages the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Pages::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/content/controllers/RuleController.php b/backend/modules/content/controllers/RuleController.php index 5cb141d85..516fde8ea 100644 --- a/backend/modules/content/controllers/RuleController.php +++ b/backend/modules/content/controllers/RuleController.php @@ -17,107 +17,115 @@ class RuleController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } - /** - * Lists all Rule models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new RuleSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Rule models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new RuleSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Rule model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Rule model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Rule(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single Rule model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Rule model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Rule(); - /** - * Updates an existing Rule model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Rule model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Rule model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the Rule model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Rule the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Rule::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException('The requested page does not exist.'); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Rule model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Rule model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Rule the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Rule::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException('The requested page does not exist.'); + } } diff --git a/backend/modules/content/controllers/VpnTemplateController.php b/backend/modules/content/controllers/VpnTemplateController.php index df37b5639..4017314f5 100644 --- a/backend/modules/content/controllers/VpnTemplateController.php +++ b/backend/modules/content/controllers/VpnTemplateController.php @@ -15,107 +15,118 @@ */ class VpnTemplateController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['update', 'delete'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], + ], parent::behaviors()); + } + + /** + * Lists all VpnTemplate models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new VpnTemplateSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single VpnTemplate model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new VpnTemplate model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new VpnTemplate(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Lists all VpnTemplate models. - * @return mixed - */ - public function actionIndex() - { - $searchModel = new VpnTemplateSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing VpnTemplate model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Displays a single VpnTemplate model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing VpnTemplate model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the VpnTemplate model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return VpnTemplate the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = VpnTemplate::findOne($id)) !== null) { + return $model; } - /** - * Creates a new VpnTemplate model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model = new VpnTemplate(); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } - - /** - * Updates an existing VpnTemplate model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); - } - - /** - * Deletes an existing VpnTemplate model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); - } - - /** - * Finds the VpnTemplate model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return VpnTemplate the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = VpnTemplate::findOne($id)) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); - } + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/frontend/actions/player/MailAction.php b/backend/modules/frontend/actions/player/MailAction.php index e9c5e9901..3b5de091d 100644 --- a/backend/modules/frontend/actions/player/MailAction.php +++ b/backend/modules/frontend/actions/player/MailAction.php @@ -10,6 +10,7 @@ use app\modules\frontend\models\PlayerSsl; use app\modules\frontend\models\PlayerSearch; use app\modules\settings\models\Sysconfig; +use yii\base\UserException; use yii\helpers\Html; class MailAction extends \yii\base\Action @@ -22,27 +23,17 @@ public function run(int $id, $baseURL = "https://echoctf.red/activate/") { // Get innactive players $player = $this->controller->findModel($id); - if ($player->status == 10) - { + if ($player->status == 10) { \Yii::$app->getSession()->setFlash('warning', Yii::t('app', 'Player already active skipping mail.')); - } - elseif ($player->status == 9) - { - if (\Yii::$app->sys->player_require_approval === true && $player->approval > 0 && $player->approval <= 2) - { + } elseif ($player->status == 9) { + if (\Yii::$app->sys->player_require_approval === true && $player->approval > 0 && $player->approval <= 2) { $this->approvalMail($player); - } - elseif (\Yii::$app->sys->player_require_approval === true && $player->approval > 2 && $player->approval <= 4) - { + } elseif (\Yii::$app->sys->player_require_approval === true && $player->approval > 2 && $player->approval <= 4) { $this->rejectionMail($player); - } - elseif (\Yii::$app->sys->player_require_approval !== true) - { + } elseif (\Yii::$app->sys->player_require_approval !== true) { $this->verificationMail($player); } - } - elseif ($player->status == 0) - { + } elseif ($player->status == 0) { $this->rejectionMail($player); } @@ -60,12 +51,11 @@ public function run(int $id, $baseURL = "https://echoctf.red/activate/") private function rejectionMail($player) { try { - $emailtpl=\app\modules\content\models\EmailTemplate::findOne(['name' => 'rejectVerify']); + $emailtpl = \app\modules\content\models\EmailTemplate::findOne(['name' => 'rejectVerify']); $contentHtml = $this->controller->renderPhpContent("?>" . $emailtpl->html, ['user' => $player]); $contentTxt = $this->controller->renderPhpContent("?>" . $emailtpl->txt, ['user' => $player]); - $subject=Yii::t('app', '{event_name} Account rejected', ['event_name' => trim(Yii::$app->sys->event_name)]); - if(!$player->mail($subject,$contentHtml,$contentTxt)) - { + $subject = Yii::t('app', '{event_name} Account rejected', ['event_name' => trim(Yii::$app->sys->event_name)]); + if (!$player->mail($subject, $contentHtml, $contentTxt)) { throw new \Exception('Could not send mail'); } @@ -85,15 +75,18 @@ private function rejectionMail($player) */ private function approvalMail($player) { + if ($player->verification_token == null) { + throw new UserException("The user has no token to mail"); + } + try { $activationURL = sprintf("https://%s/verify-email?token=%s", \Yii::$app->sys->offense_domain, $player->verification_token); - $emailtpl=\app\modules\content\models\EmailTemplate::findOne(['name' => 'emailVerify']); - $contentHtml = $this->controller->renderPhpContent("?>" . $emailtpl->html, ['user' => $player,'verifyLink'=>$activationURL]); - $contentTxt = $this->controller->renderPhpContent("?>" . $emailtpl->txt, ['user' => $player,'verifyLink'=>$activationURL]); - $subject=Yii::t('app', '{event_name} Account approved', ['event_name' => trim(Yii::$app->sys->event_name)]); + $emailtpl = \app\modules\content\models\EmailTemplate::findOne(['name' => 'emailVerify']); + $contentHtml = $this->controller->renderPhpContent("?>" . $emailtpl->html, ['user' => $player, 'verifyLink' => $activationURL]); + $contentTxt = $this->controller->renderPhpContent("?>" . $emailtpl->txt, ['user' => $player, 'verifyLink' => $activationURL]); + $subject = Yii::t('app', '{event_name} Account approved', ['event_name' => trim(Yii::$app->sys->event_name)]); - if(!$player->mail($subject,$contentHtml,$contentTxt)) - { + if (!$player->mail($subject, $contentHtml, $contentTxt)) { return; } @@ -112,15 +105,18 @@ private function approvalMail($player) */ private function verificationMail($player) { + if ($player->verification_token == null) { + throw new UserException("The user has no token to mail"); + } + try { $activationURL = sprintf("https://%s/verify-email?token=%s", \Yii::$app->sys->offense_domain, $player->verification_token); - $emailtpl=\app\modules\content\models\EmailTemplate::findOne(['name' => 'emailVerify']); - $contentHtml = $this->controller->renderPhpContent("?>" . $emailtpl->html, ['user' => $player,'verifyLink'=>$activationURL]); - $contentTxt = $this->controller->renderPhpContent("?>" . $emailtpl->txt, ['user' => $player,'verifyLink'=>$activationURL]); - $subject=Yii::t('app', 'Account registration for {event_name}', ['event_name' => trim(Yii::$app->sys->event_name)]); + $emailtpl = \app\modules\content\models\EmailTemplate::findOne(['name' => 'emailVerify']); + $contentHtml = $this->controller->renderPhpContent("?>" . $emailtpl->html, ['user' => $player, 'verifyLink' => $activationURL]); + $contentTxt = $this->controller->renderPhpContent("?>" . $emailtpl->txt, ['user' => $player, 'verifyLink' => $activationURL]); + $subject = Yii::t('app', 'Account registration for {event_name}', ['event_name' => trim(Yii::$app->sys->event_name)]); - if(!$player->mail($subject,$contentHtml,$contentTxt)) - { + if (!$player->mail($subject, $contentHtml, $contentTxt)) { throw new \Exception('Could not send mail'); } @@ -129,5 +125,4 @@ private function verificationMail($player) \Yii::$app->getSession()->setFlash('error', Yii::t('app', 'Failed to mail player. {exception}', ['exception' => Html::encode($e->getMessage())])); } } - } diff --git a/backend/modules/frontend/controllers/PlayerController.php b/backend/modules/frontend/controllers/PlayerController.php index 2d8686403..30d8f6962 100644 --- a/backend/modules/frontend/controllers/PlayerController.php +++ b/backend/modules/frontend/controllers/PlayerController.php @@ -32,7 +32,7 @@ public function behaviors() 'class' => \yii\filters\AccessControl::class, 'rules' => [ '00filtered-actions' => [ - 'actions' => ['mail', 'approve', 'reject', 'export'], + 'actions' => ['mail', 'approve', 'reject', 'export', 'notify', 'set-deleted', 'disconnect-vpn', 'generate-ssl', 'ajax-search','mail-filtered'], 'allow' => true, 'roles' => ['@'], ] @@ -616,11 +616,13 @@ public function actionMailFiltered() { $searchModel = new PlayerSearch(); $query = $searchModel->search(['PlayerSearch' => Yii::$app->request->post()]); + $query->query->innerJoinWith('emailToken', false); + $query->query->andFilterWhere(['IS NOT', 'emailToken.token', NULL]); $query->query->andFilterWhere([ 'player.approval' => [1, 3], ]); - //$query->pagination = false; - $query->pagination = ['pageSize' => 5]; + $query->pagination = false; + //$query->pagination = ['pageSize' => 5]; if (intval($query->count) === intval(Player::find()->count())) { Yii::$app->session->setFlash('error', Yii::t('app', 'You have attempted to mail all the players.')); return $this->redirect(['index']); @@ -708,7 +710,7 @@ public function actionAjaxSearch($term, $load = false, $active = null, $status = function ($model) { return [ 'id' => $model->id, - 'pid'=>$model->profile->id, + 'pid' => $model->profile->id, 'label' => sprintf("(id: %d / pid: %d) %s <%s>%s", $model->id, $model->profile->id, $model->username, $model->email, $model->status === 10 ? '' : ' (innactive)'), ]; } @@ -748,7 +750,7 @@ public function actionNotify($id) private function notifyLogic($player, $notificationModel, $ovpn = false, $online = false) { - if ($ovpn && $player->playerLast->vpn_local_address===null) + if ($ovpn && $player->playerLast->vpn_local_address === null) return null; if ($online && !boolval($player->online)) return null; diff --git a/backend/modules/frontend/controllers/ProfileController.php b/backend/modules/frontend/controllers/ProfileController.php index f23c4b624..9fba53c01 100644 --- a/backend/modules/frontend/controllers/ProfileController.php +++ b/backend/modules/frontend/controllers/ProfileController.php @@ -32,7 +32,7 @@ public function behaviors() 'class' => \yii\filters\AccessControl::class, 'rules' => [ '00filtered-actions'=>[ - 'actions' => ['view-full','target-progress','score-monthly','headshots','vpn-history','spin-history','notifications'], + 'actions' => ['view-full','target-progress','score-monthly','headshots','vpn-history','spin-history','notifications','stream-lag','vpn-history-duplicates'], 'allow' => true, 'roles' => ['@'], ] diff --git a/backend/modules/frontend/controllers/TeamController.php b/backend/modules/frontend/controllers/TeamController.php index 604253131..39a3f8067 100644 --- a/backend/modules/frontend/controllers/TeamController.php +++ b/backend/modules/frontend/controllers/TeamController.php @@ -24,7 +24,17 @@ class TeamController extends \app\components\BaseController */ public function behaviors() { - return ArrayHelper::merge(parent::behaviors(), [ + return ArrayHelper::merge([ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + '00filtered-actions' => [ + 'actions' => ['free-player-ajax-search','notify','ajax-search'], + 'allow' => true, + 'roles' => ['@'], + ] + ], + ], 'rules' => [ 'class' => 'yii\filters\AjaxFilter', 'only' => ['free-player-ajax-search'] @@ -36,7 +46,7 @@ public function behaviors() 'repopulate-stream' => ['POST'], ], ], - ]); + ], parent::behaviors()); } /** @@ -187,13 +197,11 @@ public function actionToggleAcademic($id) $trans = Yii::$app->db->beginTransaction(); try { $model->updateAttributes(['academic' => ($model->academic + 1) % \Yii::$app->sys->academic_grouping]); - foreach($model->teamPlayers as $p) - { - $p->updateAttributes(['academic'=>$model->academic]); + foreach ($model->teamPlayers as $p) { + $p->updateAttributes(['academic' => $model->academic]); } $trans->commit(); \Yii::$app->getSession()->setFlash('success', Yii::t('app', 'Team changed academic grouping.')); - } catch (\Exception $e) { $trans->rollBack(); \Yii::$app->getSession()->setFlash('error', Yii::t('app', 'Failed to update academic grouping for team')); diff --git a/backend/modules/frontend/models/Player.php b/backend/modules/frontend/models/Player.php index 97aec5de9..60782458f 100644 --- a/backend/modules/frontend/models/Player.php +++ b/backend/modules/frontend/models/Player.php @@ -54,6 +54,7 @@ public function getAuthKey() public function saveWithSsl() { if (!$this->save()) { + Yii::error($this->getErrorSummary(true)); return false; } @@ -63,6 +64,7 @@ public function saveWithSsl() if ($playerSsl->save()) { return $playerSsl->refresh(); } + Yii::error($playerSsl->getErrorSummary(true)); return false; } @@ -144,6 +146,11 @@ public function createTeam($team_name, $approved) if (!$tp->save()) echo Yii::t('app', "Error saving team player\n"); + $ti = new \app\modules\frontend\models\TeamInvite(['team_id' => $team->id, 'token' => Yii::$app->security->generateRandomString(8)]); + if (!$ti->save()) { + echo Yii::t('app', "Error saving team invite\n"); + Yii::error($ti->getErrorSummary(true)); + } } /** diff --git a/backend/modules/frontend/models/PlayerAR.php b/backend/modules/frontend/models/PlayerAR.php index 878030d40..5675a39f6 100644 --- a/backend/modules/frontend/models/PlayerAR.php +++ b/backend/modules/frontend/models/PlayerAR.php @@ -526,7 +526,22 @@ public function getAuthKey() */ public function getApiToken() { - return $this->hasOne(PlayerToken::class, ['player_id' => 'id'])->andWhere(['type' => 'API']); + return $this->hasOne(PlayerToken::class, ['player_id' => 'id'])->from(['apiToken' => PlayerToken::tableName()])->andWhere(['apiToken.type' => 'API']); } + /** + * @return \yii\db\ActiveQuery + */ + public function getEmailToken() + { + return $this->hasOne(PlayerToken::class, ['player_id' => 'id'])->from(['emailToken' => PlayerToken::tableName()])->andWhere(['emailToken.type' => 'email_verification']); + } + + /** + * @return \yii\db\ActiveQuery + */ + public function getPasswordResetToken() + { + return $this->hasOne(PlayerToken::class, ['player_id' => 'id'])->from(['passwordResetToken' => PlayerToken::tableName()])->andWhere(['passwordResetToken.type' => 'password_reset']); + } } diff --git a/backend/modules/frontend/models/PlayerSsl.php b/backend/modules/frontend/models/PlayerSsl.php index 6e8bdae84..0cec4d946 100644 --- a/backend/modules/frontend/models/PlayerSsl.php +++ b/backend/modules/frontend/models/PlayerSsl.php @@ -107,7 +107,9 @@ public function generate() { $CAprivkey=array("file://".$tmpCAprivkey, null); file_put_contents($tmpCAprivkey, Yii::$app->sys->{'CA.key'}); file_put_contents($tmpCAcert, Yii::$app->sys->{'CA.crt'}); - $serial=time(); + do { + $serial=time(); + } while (self::findOne(['serial'=>$serial])!=null); $x509=openssl_csr_sign($csr, $CAcert, $CAprivkey, 3650, array('digest_alg'=>'sha256', 'config'=>Yii::getAlias('@appconfig').'/CA.cnf', 'x509_extensions'=>'usr_cert'), $serial); openssl_csr_export($csr, $csrout); openssl_x509_export($x509, $certout, false); diff --git a/backend/modules/frontend/models/Team.php b/backend/modules/frontend/models/Team.php index ce40c889d..10cf9bb84 100644 --- a/backend/modules/frontend/models/Team.php +++ b/backend/modules/frontend/models/Team.php @@ -22,6 +22,7 @@ * @property Player $owner * @property TeamPlayer[] $teamPlayers * @property Player[] $players + * @property string[] $approvedMemberIPs */ class Team extends \yii\db\ActiveRecord { @@ -98,11 +99,12 @@ public function getTeamPlayers() public function getInstances() { - return $this->hasMany(\app\modules\infrastructure\models\TargetInstance::class, ['player_id' => 'player_id']) - ->via('teamPlayers', function($query) { - $query->andWhere(['approved' => 1]); - }); + return $this->hasMany(\app\modules\infrastructure\models\TargetInstance::class, ['player_id' => 'player_id']) + ->via('teamPlayers', function ($query) { + $query->andWhere(['approved' => 1]); + }); } + /** * @return \yii\db\ActiveQuery */ @@ -111,6 +113,22 @@ public function getApprovedMembers() return $this->hasMany(TeamPlayer::class, ['team_id' => 'id'])->andWhere(['approved' => 1]); } + /** + * Get array of all approved members currently online + * + * @return array + */ + public function getApprovedMemberIPs() + { + $ips=[]; + foreach ($this->approvedMembers as $tp) { + if ((int)$tp->player->last->vpn_local_address !== 0) { + $ips[] = long2ip($tp->player->last->vpn_local_address); + } + } + return $ips; + } + /** * @return \yii\db\ActiveQuery */ @@ -197,6 +215,6 @@ public function getInvite() */ public function repopulateStream() { - return \Yii::$app->db->createCommand('CALL repopulate_team_stream(:tid)',[':tid'=>$this->id])->execute(); + return \Yii::$app->db->createCommand('CALL repopulate_team_stream(:tid)', [':tid' => $this->id])->execute(); } } diff --git a/backend/modules/frontend/views/player/index.php b/backend/modules/frontend/views/player/index.php index 8b684d02c..9238f5ae0 100644 --- a/backend/modules/frontend/views/player/index.php +++ b/backend/modules/frontend/views/player/index.php @@ -22,7 +22,7 @@

'btn btn-success']) ?> 'btn btn-info']) ?> - 'btn','style' => 'background: #4040bf; color: white;',]) ?> + 'btn', 'style' => 'background: #4040bf; color: white;',]) ?> 'btn', 'style' => 'background: #4d246f; color: white;', @@ -45,7 +45,7 @@ 'rowOptions' => function ($model, $key, $index, $grid) { // $model is the current data model being rendered // check your condition in the if like `if($model->hasMedicalRecord())` which could be a method of model class which checks for medical records. - $tmpObj = clone ($model); + $tmpObj = clone($model); $tmpObj->scenario = 'validator'; if (!$tmpObj->validate()) { unset($tmpObj); @@ -112,9 +112,12 @@ [ 'attribute' => 'approval', 'filter' => $searchModel::APPROVAL, + 'format' => 'html', 'visible' => Yii::$app->sys->player_require_approval === true, 'value' => function ($model) { - return $model::APPROVAL[$model->approval]; + if ($model->status === 10) + return "(" . $model::APPROVAL[$model->approval] . ")"; + return "" . $model::APPROVAL[$model->approval] . ""; } ], @@ -138,7 +141,7 @@ return false; }, 'disconnect-vpn' => function ($model) { - if ($model->last->vpn_local_address !== null && $model->disconnectQueue===null) return true; + if ($model->last->vpn_local_address !== null && $model->disconnectQueue === null) return true; return false; }, 'view' => function ($model) { @@ -157,7 +160,7 @@ return false; }, 'mail' => function ($model) { - if ($model->status == 10 || $model->approval == 0) return false; + if ($model->status == 10 || $model->status == 0 || $model->approval == 0 || ( $model->emailToken == null && $model->passwordResetToken == null)) return false; return true; }, 'delete' => function ($model) { diff --git a/backend/modules/gameplay/controllers/ChallengeController.php b/backend/modules/gameplay/controllers/ChallengeController.php index 893a293c5..281693787 100644 --- a/backend/modules/gameplay/controllers/ChallengeController.php +++ b/backend/modules/gameplay/controllers/ChallengeController.php @@ -22,167 +22,171 @@ class ChallengeController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } - /** - * Lists all Challenge models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new ChallengeSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Challenge models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new ChallengeSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Challenge model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - $query=Question::find()->joinWith('challenge'); - - $query->select('question.*,(SELECT COUNT(question_id) FROM player_question WHERE question.id=player_question.question_id) as answered'); - // add conditions that should always apply here - - $dataProvider=new ActiveDataProvider([ - 'query' => $query, - ]); - $query->andFilterWhere([ - 'question.challenge_id' => $id, - ]); - - $dataProvider->setSort([ - 'defaultOrder' => ['weight' => SORT_ASC,'id'=>SORT_ASC], - 'attributes' => array_merge( - $dataProvider->getSort()->attributes, - [ - 'challengename' => [ - 'asc' => ['challengename' => SORT_ASC], - 'desc' => ['challengename' => SORT_DESC], - ], - 'answered' => [ - 'asc' => ['answered' => SORT_ASC], - 'desc' => ['answered' => SORT_DESC], - ], - ] - ), - ]); - - return $this->render('view', [ - 'model' => $this->findModel($id), - 'questionProvider'=>$dataProvider, - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Challenge model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Challenge(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - $model->file=UploadedFile::getInstance($model, 'file'); - try - { - if($model->file) - { - if(trim($model->filename)==='') - { - $model->filename=$model->id; - $model->updateAttributes(['filename'=>$model->id]); - } - $model->file->saveAs(Yii::getAlias(Yii::$app->sys->challenge_home).'/'.$model->filename); - } - Yii::$app->session->addFlash('success', Yii::t('app','Challenge [{name}] created',['name'=>Html::encode($model->name)])); - Yii::$app->session->addFlash('warning', Yii::t('app','Don\'t forget to create a question for the challenge.')); - } - catch(\Exception $e) - { - Yii::$app->session->setFlash('error', Yii::t('app','Failed to create challenge [{name}]',['name'=>Html::encode($model->name)])); - } - return $this->redirect(['view', 'id' => $model->id]); - } + /** + * Displays a single Challenge model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + $query = Question::find()->joinWith('challenge'); + + $query->select('question.*,(SELECT COUNT(question_id) FROM player_question WHERE question.id=player_question.question_id) as answered'); + // add conditions that should always apply here + + $dataProvider = new ActiveDataProvider([ + 'query' => $query, + ]); + $query->andFilterWhere([ + 'question.challenge_id' => $id, + ]); + + $dataProvider->setSort([ + 'defaultOrder' => ['weight' => SORT_ASC, 'id' => SORT_ASC], + 'attributes' => array_merge( + $dataProvider->getSort()->attributes, + [ + 'challengename' => [ + 'asc' => ['challengename' => SORT_ASC], + 'desc' => ['challengename' => SORT_DESC], + ], + 'answered' => [ + 'asc' => ['answered' => SORT_ASC], + 'desc' => ['answered' => SORT_DESC], + ], + ] + ), + ]); + + return $this->render('view', [ + 'model' => $this->findModel($id), + 'questionProvider' => $dataProvider, + ]); + } - return $this->render('create', [ - 'model' => $model, - ]); + /** + * Creates a new Challenge model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Challenge(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + $model->file = UploadedFile::getInstance($model, 'file'); + try { + if ($model->file) { + if (trim($model->filename) === '') { + $model->filename = $model->id; + $model->updateAttributes(['filename' => $model->id]); + } + $model->file->saveAs(Yii::getAlias(Yii::$app->sys->challenge_home) . '/' . $model->filename); + } + Yii::$app->session->addFlash('success', Yii::t('app', 'Challenge [{name}] created', ['name' => Html::encode($model->name)])); + Yii::$app->session->addFlash('warning', Yii::t('app', 'Don\'t forget to create a question for the challenge.')); + } catch (\Exception $e) { + Yii::$app->session->setFlash('error', Yii::t('app', 'Failed to create challenge [{name}]', ['name' => Html::encode($model->name)])); + } + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Updates an existing Challenge model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - $model->file=UploadedFile::getInstance($model, 'file'); - if($model->file !== null) - { - if(trim($model->filename)==='') - { - $model->filename=$model->id; - $model->updateAttributes(['filename'=>$model->id]); - } - $model->file->saveAs(Yii::getAlias(Yii::$app->sys->challenge_home).'/'.$model->filename); - } - Yii::$app->session->addFlash('success', Yii::t('app','Challenge [{name}] updated',['name'=>Html::encode($model->name)])); - return $this->redirect(['view', 'id' => $model->id]); - } + return $this->render('create', [ + 'model' => $model, + ]); + } - return $this->render('update', [ - 'model' => $model, - ]); + /** + * Updates an existing Challenge model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + $model->file = UploadedFile::getInstance($model, 'file'); + if ($model->file !== null) { + if (trim($model->filename) === '') { + $model->filename = $model->id; + $model->updateAttributes(['filename' => $model->id]); + } + $model->file->saveAs(Yii::getAlias(Yii::$app->sys->challenge_home) . '/' . $model->filename); + } + Yii::$app->session->addFlash('success', Yii::t('app', 'Challenge [{name}] updated', ['name' => Html::encode($model->name)])); + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Challenge model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); - } + return $this->render('update', [ + 'model' => $model, + ]); + } - /** - * Finds the Challenge model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Challenge the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Challenge::findOne($id)) !== null) - { - return $model; - } + /** + * Deletes an existing Challenge model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } - throw new NotFoundHttpException(Yii::t('app','The requested page does not exist.')); + /** + * Finds the Challenge model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Challenge the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Challenge::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/gameplay/controllers/QuestionController.php b/backend/modules/gameplay/controllers/QuestionController.php index d02804c92..83a49e414 100644 --- a/backend/modules/gameplay/controllers/QuestionController.php +++ b/backend/modules/gameplay/controllers/QuestionController.php @@ -17,113 +17,124 @@ class QuestionController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + + ]); + } - /** - * Lists all Question models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new QuestionSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all Question models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new QuestionSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single Question model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new Question model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Question(); - if(\app\modules\gameplay\models\Challenge::find()->count() == 0) - { - // If there are no player redirect to create player page - Yii::$app->session->setFlash('warning', Yii::t('app',"No Challenges found create one first.")); - return $this->redirect(['/frontend/challenge/create']); - } - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); + /** + * Displays a single Question model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Question model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Question(); + if (\app\modules\gameplay\models\Challenge::find()->count() == 0) { + // If there are no player redirect to create player page + Yii::$app->session->setFlash('warning', Yii::t('app', "No Challenges found create one first.")); + return $this->redirect(['/frontend/challenge/create']); } - /** - * Updates an existing Question model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing Question model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Question model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the Question model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Question the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Question::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app','The requested page does not exist.')); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Question model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Question model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Question the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Question::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/gameplay/controllers/TreasureController.php b/backend/modules/gameplay/controllers/TreasureController.php index 8b25e255a..9937f722e 100644 --- a/backend/modules/gameplay/controllers/TreasureController.php +++ b/backend/modules/gameplay/controllers/TreasureController.php @@ -19,7 +19,21 @@ class TreasureController extends \app\components\BaseController */ public function behaviors() { - return ArrayHelper::merge(parent::behaviors(), []); + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); } /** @@ -47,7 +61,7 @@ public function actionValidate() if ($string !== "") { $secretKey = Yii::$app->sys->treasure_secret_key; $results = Yii::$app->db->createCommand("select treasure.id,player.id as player_id from treasure,player where md5(HEX(AES_ENCRYPT(CONCAT(code, player.id), :secretKey))) LIKE :code", [':secretKey' => $secretKey, ':code' => $string])->queryOne(); - if ($results === [] || $results===false) { + if ($results === [] || $results === false) { Yii::$app->session->setFlash('warning', Yii::t('app', "Code not found.")); } else { $player = \app\modules\frontend\models\Player::findOne($results['player_id']); @@ -56,21 +70,18 @@ public function actionValidate() 'username' => $player->username, 'actions' => false ]); - if($player->teamPlayer!==NULL) - { - $msg = sprintf('Code belongs to player [%s] from team [%s] for target %s and treasure %s', $profileLink,$player->teamPlayer->team->name, $treasure->target->name, $treasure->name); - } - else - { + if ($player->teamPlayer !== NULL) { + $msg = sprintf('Code belongs to player [%s] from team [%s] for target %s and treasure %s', $profileLink, $player->teamPlayer->team->name, $treasure->target->name, $treasure->name); + } else { $msg = sprintf('Code belongs to player [%s] for target %s and treasure %s', $profileLink, $treasure->target->name, $treasure->name); } Yii::$app->session->setFlash('success', $msg); - $string=''; + $string = ''; } } - return $this->render('validate',['code'=>$string]); + return $this->render('validate', ['code' => $string]); } /** diff --git a/backend/modules/infrastructure/controllers/TargetMetadataController.php b/backend/modules/infrastructure/controllers/TargetMetadataController.php index 25f062ab9..a7ee6c011 100644 --- a/backend/modules/infrastructure/controllers/TargetMetadataController.php +++ b/backend/modules/infrastructure/controllers/TargetMetadataController.php @@ -19,102 +19,116 @@ class TargetMetadataController extends \app\components\BaseController */ public function behaviors() { - return ArrayHelper::merge(parent::behaviors(), []); + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); } - /** - * Lists all TargetMetadata models. - * @return mixed - */ + /** + * Lists all TargetMetadata models. + * @return mixed + */ public function actionIndex() { - $searchModel = new TargetMetadataSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + $searchModel = new TargetMetadataSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); } - /** - * Displays a single TargetMetadata model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ + /** + * Displays a single TargetMetadata model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ public function actionView($id) { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); } - /** - * Creates a new TargetMetadata model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ + /** + * Creates a new TargetMetadata model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ public function actionCreate() { - $model = new TargetMetadata(); + $model = new TargetMetadata(); if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->target_id]); + return $this->redirect(['view', 'id' => $model->target_id]); } - return $this->render('create', [ - 'model' => $model, - ]); + return $this->render('create', [ + 'model' => $model, + ]); } - /** - * Updates an existing TargetMetadata model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ + /** + * Updates an existing TargetMetadata model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ public function actionUpdate($id) { - $model = $this->findModel($id); + $model = $this->findModel($id); if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->target_id]); + return $this->redirect(['view', 'id' => $model->target_id]); } - return $this->render('update', [ - 'model' => $model, - ]); + return $this->render('update', [ + 'model' => $model, + ]); } - /** - * Deletes an existing TargetMetadata model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ + /** + * Deletes an existing TargetMetadata model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ public function actionDelete($id) { - $this->findModel($id)->delete(); + $this->findModel($id)->delete(); - return $this->redirect(['index']); + return $this->redirect(['index']); } - /** - * Finds the TargetMetadata model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return TargetMetadata the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ + /** + * Finds the TargetMetadata model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return TargetMetadata the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ protected function findModel($id) { if (($model = TargetMetadata::findOne($id)) !== null) { - return $model; + return $model; } - throw new NotFoundHttpException('The requested page does not exist.'); + throw new NotFoundHttpException('The requested page does not exist.'); } } diff --git a/backend/modules/infrastructure/models/TargetInstanceQuery.php b/backend/modules/infrastructure/models/TargetInstanceQuery.php index b2551b250..da1067901 100644 --- a/backend/modules/infrastructure/models/TargetInstanceQuery.php +++ b/backend/modules/infrastructure/models/TargetInstanceQuery.php @@ -9,14 +9,50 @@ */ class TargetInstanceQuery extends \yii\db\ActiveQuery { + /** + * Filters TargetInstances to those that are active + * and whose team has at least one approved member with vpn_local_address != 0 + */ + public function withApprovedMemberHeartbeat() + { + return $this + // join to the team_instance player with teamPlayer + ->innerJoin(['tp' => 'team_player'], 'target_instance.player_id = tp.player_id AND tp.approved = 1') + // join to the team + ->innerJoin(['t' => 'team'], 'tp.team_id = t.id') + // join to approved members of the team + ->innerJoin(['am' => 'team_player'], 'am.team_id = t.id AND am.approved = 1') + // join approved member's last + ->innerJoin(['al' => 'player_last'], 'al.id = am.player_id and al.vpn_local_address is not null') + // ensure the approved member has a vpn_local_address + ->distinct(); + } + public function active() { - return $this->andWhere('[[ip]] IS NOT NULL')->andWhere('[[reboot]]!=2'); + return $this->andWhere('target_instance.[[ip]] IS NOT NULL')->andWhere('target_instance.[[reboot]]!=2'); + } + + public function last_updated(int $seconds_ago = 1) + { + return $this->andWhere(['<', 'target_instance.[[updated_at]]', new \yii\db\Expression("NOW() - INTERVAL $seconds_ago SECOND")]); } - public function pending_action($minutes_ago = 60) + public function pending_action(int $seconds_ago = 1) { - return $this->andWhere('[[ip]] IS NULL')->orWhere(['>', 'reboot', 0])->orWhere(['<', 'updated_at', new \yii\db\Expression("NOW() - INTERVAL $minutes_ago MINUTE")]); + return $this->addSelect([ + 'target_instance.*', + 'reboot' => new \yii\db\Expression( + "IF(target_instance.updated_at < (NOW() - INTERVAL :seconds SECOND), 2, target_instance.reboot)", + [':seconds' => $seconds_ago] + ), + ]) + ->andWhere([ + 'or', + ['target_instance.ip' => null], + ['>', 'target_instance.reboot', 0], + ['<', 'target_instance.updated_at', new \yii\db\Expression("NOW() - INTERVAL $seconds_ago SECOND")] + ]); } diff --git a/backend/modules/infrastructure/views/target-instance/view.php b/backend/modules/infrastructure/views/target-instance/view.php index 1a6c5f3ae..92ebc9210 100644 --- a/backend/modules/infrastructure/views/target-instance/view.php +++ b/backend/modules/infrastructure/views/target-instance/view.php @@ -66,7 +66,7 @@ 'team_allowed:boolean', [ 'label' => 'encrypted flags', - 'visible'=>$model->target->dynamic_treasures, + 'visible'=>$model->target->dynamic_treasures && \Yii::$app->user->identity->isAdmin, 'format' => 'raw', 'value' => function ($model) { $lines=[]; diff --git a/backend/modules/settings/controllers/CountryController.php b/backend/modules/settings/controllers/CountryController.php index 0f4a9e51c..5bd83eac0 100644 --- a/backend/modules/settings/controllers/CountryController.php +++ b/backend/modules/settings/controllers/CountryController.php @@ -14,110 +14,121 @@ */ class CountryController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } + + /** + * Lists all Country models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new CountrySearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single Country model. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Country model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Country(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Lists all Country models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new CountrySearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Country model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Displays a single Country model. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Country model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Country model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param string $id + * @return Country the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Country::findOne($id)) !== null) { + return $model; } - /** - * Creates a new Country model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Country(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } - - /** - * Updates an existing Country model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); - } - - /** - * Deletes an existing Country model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); - } - - /** - * Finds the Country model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param string $id - * @return Country the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Country::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); - } + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/settings/controllers/DisabledRouteController.php b/backend/modules/settings/controllers/DisabledRouteController.php index b83d419c6..5390a6223 100644 --- a/backend/modules/settings/controllers/DisabledRouteController.php +++ b/backend/modules/settings/controllers/DisabledRouteController.php @@ -17,107 +17,118 @@ class DisabledRouteController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } - /** - * Lists all DisabledRoute models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new DisabledRouteSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all DisabledRoute models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new DisabledRouteSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single DisabledRoute model. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new DisabledRoute model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new DisabledRoute(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->route]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single DisabledRoute model. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new DisabledRoute model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new DisabledRoute(); - /** - * Updates an existing DisabledRoute model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->route]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->route]); } - /** - * Deletes an existing DisabledRoute model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing DisabledRoute model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->route]); } - /** - * Finds the DisabledRoute model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param string $id - * @return DisabledRoute the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=DisabledRoute::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException('The requested page does not exist.'); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing DisabledRoute model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the DisabledRoute model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param string $id + * @return DisabledRoute the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = DisabledRoute::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException('The requested page does not exist.'); + } } diff --git a/backend/modules/settings/controllers/OpenvpnController.php b/backend/modules/settings/controllers/OpenvpnController.php index 2cf6ddb77..b14886aac 100644 --- a/backend/modules/settings/controllers/OpenvpnController.php +++ b/backend/modules/settings/controllers/OpenvpnController.php @@ -15,107 +15,121 @@ */ class OpenvpnController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } + + /** + * Lists all Openvpn models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new OpenvpnSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single Openvpn model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new Openvpn model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Openvpn(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Lists all Openvpn models. - * @return mixed - */ - public function actionIndex() - { - $searchModel = new OpenvpnSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Openvpn model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Displays a single Openvpn model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Openvpn model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Openvpn model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return Openvpn the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Openvpn::findOne($id)) !== null) { + return $model; } - /** - * Creates a new Openvpn model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model = new Openvpn(); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } - - /** - * Updates an existing Openvpn model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); - } - - /** - * Deletes an existing Openvpn model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); - } - - /** - * Finds the Openvpn model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return Openvpn the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = Openvpn::findOne($id)) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); - } + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/settings/controllers/PlayerDisabledrouteController.php b/backend/modules/settings/controllers/PlayerDisabledrouteController.php index a780accc2..8ed11c0ad 100644 --- a/backend/modules/settings/controllers/PlayerDisabledrouteController.php +++ b/backend/modules/settings/controllers/PlayerDisabledrouteController.php @@ -14,107 +14,121 @@ */ class PlayerDisabledrouteController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } + + /** + * Lists all PlayerDisabledroute models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new PlayerDisabledrouteSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Displays a single PlayerDisabledroute model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new PlayerDisabledroute model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new PlayerDisabledroute(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Lists all PlayerDisabledroute models. - * @return mixed - */ - public function actionIndex() - { - $searchModel = new PlayerDisabledrouteSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing PlayerDisabledroute model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Displays a single PlayerDisabledroute model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing PlayerDisabledroute model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the PlayerDisabledroute model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return PlayerDisabledroute the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = PlayerDisabledroute::findOne($id)) !== null) { + return $model; } - /** - * Creates a new PlayerDisabledroute model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model = new PlayerDisabledroute(); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } - - /** - * Updates an existing PlayerDisabledroute model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); - } - - /** - * Deletes an existing PlayerDisabledroute model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); - } - - /** - * Finds the PlayerDisabledroute model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return PlayerDisabledroute the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = PlayerDisabledroute::findOne($id)) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); - } + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/settings/controllers/SysconfigController.php b/backend/modules/settings/controllers/SysconfigController.php index 233701be8..6ff1a10fc 100644 --- a/backend/modules/settings/controllers/SysconfigController.php +++ b/backend/modules/settings/controllers/SysconfigController.php @@ -15,129 +15,137 @@ */ class SysconfigController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } + + /** + * Lists all Sysconfig models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new SysconfigSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); + + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } + + /** + * Creates a new Sysconfig model. + * If creation is successful, the browser will be redirected to the 'index' page. + * @return mixed + */ + public function actionCreate() + { + $model = new Sysconfig(); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['index']); } - /** - * Lists all Sysconfig models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new SysconfigSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } - - /** - * Creates a new Sysconfig model. - * If creation is successful, the browser will be redirected to the 'index' page. - * @return mixed - */ - public function actionCreate() - { - $model=new Sysconfig(); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['index']); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } - - /** - * Updates an existing Sysconfig model. - * If update is successful, the browser will be redirected to the 'index' page. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - - if($model->load(Yii::$app->request->post()) && $model->save()) - { - Yii::$app->session->setFlash('success', Yii::t('app','{id} updated.',['id'=>$model->id])); - if($id!==$model->id) - { - return $this->redirect(['update','id'=>$model->id]); - } - } - - return $this->render('update', [ - 'model' => $model, - ]); - } - - /** - * Creates/Updates a Sysconfig set model. - * If update is successful, the browser will be redirected to the 'index' page. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionConfigure() - { - $model=new ConfigureForm(); - if($model->load(Yii::$app->request->post()) && $model->save()) - { - Yii::$app->session->setFlash('success',Yii::t('app','Configuration saved')); - return $this->redirect(['configure']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing Sysconfig model. + * If update is successful, the browser will be redirected to the 'index' page. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + Yii::$app->session->setFlash('success', Yii::t('app', '{id} updated.', ['id' => $model->id])); + if ($id !== $model->id) { + return $this->redirect(['update', 'id' => $model->id]); } - - return $this->render('configure', [ - 'model' => $model, - ]); } - /** - * Deletes an existing Sysconfig model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param string $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Creates/Updates a Sysconfig set model. + * If update is successful, the browser will be redirected to the 'index' page. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionConfigure() + { + $model = new ConfigureForm(); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + Yii::$app->session->setFlash('success', Yii::t('app', 'Configuration saved')); + return $this->redirect(['configure']); } - /** - * Finds the Sysconfig model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param string $id - * @return Sysconfig the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=Sysconfig::findOne($id)) !== null) - { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app','The requested page does not exist.')); + return $this->render('configure', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing Sysconfig model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param string $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the Sysconfig model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param string $id + * @return Sysconfig the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = Sysconfig::findOne($id)) !== null) { + return $model; } - protected function stripYii() - { - $array=explode("\n", trim(ob_get_clean())); - for($i=0;$i < 3;$i++) array_shift($array); - return array_map('trim', $array); - } + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } + protected function stripYii() + { + $array = explode("\n", trim(ob_get_clean())); + for ($i = 0; $i < 3; $i++) array_shift($array); + return array_map('trim', $array); + } } diff --git a/backend/modules/settings/controllers/UrlRouteController.php b/backend/modules/settings/controllers/UrlRouteController.php index e8a27e15f..446f39a3f 100644 --- a/backend/modules/settings/controllers/UrlRouteController.php +++ b/backend/modules/settings/controllers/UrlRouteController.php @@ -9,112 +9,127 @@ use yii\web\NotFoundHttpException; use yii\filters\VerbFilter; use yii\helpers\ArrayHelper; + /** * UrlRouteController implements the CRUD actions for UrlRoute model. */ class UrlRouteController extends \app\components\BaseController { - /** - * {@inheritdoc} - */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } + /** + * {@inheritdoc} + */ + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } - /** - * Lists all UrlRoute models. - * @return mixed - */ - public function actionIndex() - { - $searchModel = new UrlRouteSearch(); - $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } + /** + * Lists all UrlRoute models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new UrlRouteSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Displays a single UrlRoute model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - /** - * Creates a new UrlRoute model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model = new UrlRoute(); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Displays a single UrlRoute model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } + + /** + * Creates a new UrlRoute model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new UrlRoute(); - /** - * Updates an existing UrlRoute model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model = $this->findModel($id); - - if ($model->load(Yii::$app->request->post()) && $model->save()) { - return $this->redirect(['view', 'id' => $model->id]); - } - - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing UrlRoute model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); - - return $this->redirect(['index']); + return $this->render('create', [ + 'model' => $model, + ]); + } + + /** + * Updates an existing UrlRoute model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Finds the UrlRoute model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return UrlRoute the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if (($model = UrlRoute::findOne($id)) !== null) { - return $model; - } - - throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing UrlRoute model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); + + return $this->redirect(['index']); + } + + /** + * Finds the UrlRoute model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return UrlRoute the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = UrlRoute::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException(Yii::t('app', 'The requested page does not exist.')); + } } diff --git a/backend/modules/settings/controllers/UserController.php b/backend/modules/settings/controllers/UserController.php index cea3f74ba..78d1d6602 100644 --- a/backend/modules/settings/controllers/UserController.php +++ b/backend/modules/settings/controllers/UserController.php @@ -17,105 +17,116 @@ class UserController extends \app\components\BaseController /** * {@inheritdoc} */ - public function behaviors() - { - return ArrayHelper::merge(parent::behaviors(),[]); - } - - /** - * Lists all User models. - * @return mixed - */ - public function actionIndex() - { - $searchModel=new UserSearch(); - $dataProvider=$searchModel->search(Yii::$app->request->queryParams); + public function behaviors() + { + return ArrayHelper::merge(parent::behaviors(), [ + 'access' => [ + 'class' => \yii\filters\AccessControl::class, + 'rules' => [ + 'authActions' => [ + 'allow' => true, + 'actions' => ['index', 'view'], + 'roles' => ['@'], + 'matchCallback' => function () { + return \Yii::$app->user->identity->isAdmin; + }, + ], + ], + ], + ]); + } - return $this->render('index', [ - 'searchModel' => $searchModel, - 'dataProvider' => $dataProvider, - ]); - } - - /** - * Displays a single User model. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionView($id) - { - return $this->render('view', [ - 'model' => $this->findModel($id), - ]); - } + /** + * Lists all User models. + * @return mixed + */ + public function actionIndex() + { + $searchModel = new UserSearch(); + $dataProvider = $searchModel->search(Yii::$app->request->queryParams); - /** - * Creates a new User model. - * If creation is successful, the browser will be redirected to the 'view' page. - * @return mixed - */ - public function actionCreate() - { - $model=new User(); + return $this->render('index', [ + 'searchModel' => $searchModel, + 'dataProvider' => $dataProvider, + ]); + } - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } + /** + * Displays a single User model. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionView($id) + { + return $this->render('view', [ + 'model' => $this->findModel($id), + ]); + } - return $this->render('create', [ - 'model' => $model, - ]); - } + /** + * Creates a new User model. + * If creation is successful, the browser will be redirected to the 'view' page. + * @return mixed + */ + public function actionCreate() + { + $model = new User(); - /** - * Updates an existing User model. - * If update is successful, the browser will be redirected to the 'view' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionUpdate($id) - { - $model=$this->findModel($id); - if($model->load(Yii::$app->request->post()) && $model->save()) - { - return $this->redirect(['view', 'id' => $model->id]); - } - return $this->render('update', [ - 'model' => $model, - ]); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } - /** - * Deletes an existing User model. - * If deletion is successful, the browser will be redirected to the 'index' page. - * @param integer $id - * @return mixed - * @throws NotFoundHttpException if the model cannot be found - */ - public function actionDelete($id) - { - $this->findModel($id)->delete(); + return $this->render('create', [ + 'model' => $model, + ]); + } - return $this->redirect(['index']); + /** + * Updates an existing User model. + * If update is successful, the browser will be redirected to the 'view' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionUpdate($id) + { + $model = $this->findModel($id); + if ($model->load(Yii::$app->request->post()) && $model->save()) { + return $this->redirect(['view', 'id' => $model->id]); } + return $this->render('update', [ + 'model' => $model, + ]); + } + + /** + * Deletes an existing User model. + * If deletion is successful, the browser will be redirected to the 'index' page. + * @param integer $id + * @return mixed + * @throws NotFoundHttpException if the model cannot be found + */ + public function actionDelete($id) + { + $this->findModel($id)->delete(); - /** - * Finds the User model based on its primary key value. - * If the model is not found, a 404 HTTP exception will be thrown. - * @param integer $id - * @return User the loaded model - * @throws NotFoundHttpException if the model cannot be found - */ - protected function findModel($id) - { - if(($model=\app\modules\settings\models\User::findOne($id)) !== null) - { - return $model; - } + return $this->redirect(['index']); + } - throw new NotFoundHttpException('The requested page does not exist.'); + /** + * Finds the User model based on its primary key value. + * If the model is not found, a 404 HTTP exception will be thrown. + * @param integer $id + * @return User the loaded model + * @throws NotFoundHttpException if the model cannot be found + */ + protected function findModel($id) + { + if (($model = \app\modules\settings\models\User::findOne($id)) !== null) { + return $model; } + + throw new NotFoundHttpException('The requested page does not exist.'); + } } diff --git a/backend/modules/settings/models/ConfigureForm.php b/backend/modules/settings/models/ConfigureForm.php index 8eb273406..58c6ea1f0 100644 --- a/backend/modules/settings/models/ConfigureForm.php +++ b/backend/modules/settings/models/ConfigureForm.php @@ -57,7 +57,7 @@ class ConfigureForm extends Model public $leaderboard_show_zero; public $time_zone; public $target_days_new = 2; - public $target_days_updated = 1; + public $target_days_updated = 0; public $discord_news_webhook; public $pf_state_limits; public $stripe_apiKey; @@ -472,7 +472,7 @@ public function rules() [['online_timeout'], 'default', 'value' => 900], [['spins_per_day'], 'default', 'value' => 2], ['target_days_new', 'default', 'value' => 1], - ['target_days_updated', 'default', 'value' => 2], + ['target_days_updated', 'default', 'value' => 0], [['event_start', 'event_end', 'registrations_start', 'registrations_end'], 'datetime', 'format' => 'php:Y-m-d H:i:s'], [[ 'dashboard_is_home', diff --git a/backend/modules/settings/models/Sysconfig.php b/backend/modules/settings/models/Sysconfig.php index 021aad19f..605c17b87 100644 --- a/backend/modules/settings/models/Sysconfig.php +++ b/backend/modules/settings/models/Sysconfig.php @@ -56,7 +56,7 @@ public function afterFind() if ($this->val == 0 || $this->val == "") $this->val = ""; else - $this->val = Yii::$app->formatter->asDatetime($this->val, 'php:Y-m-d H:i:s', 'UTC'); + $this->val = date('Y-m-d H:i:s', $this->val); break; default: break; @@ -70,7 +70,7 @@ public function beforeSave($insert) $Q = sprintf("DROP EVENT IF EXISTS event_end_notification"); \Yii::$app->db->createCommand($Q)->execute(); if (!empty($this->val)) { - $Q = sprintf("CREATE EVENT event_end_notification ON SCHEDULE AT '%s' DO INSERT INTO `notification`(player_id,category,title,body,archived) SELECT id,'swal:info',memc_get('sysconfig:event_end_notification_title'),memc_get('sysconfig:event_end_notification_body'),0 FROM player WHERE status=10", $this->val); + $Q = sprintf("CREATE EVENT event_end_notification ON SCHEDULE AT '%s' DO BEGIN INSERT INTO `notification`(player_id,category,title,body,archived) SELECT id,'swal:info',memc_get('sysconfig:event_end_notification_title'),memc_get('sysconfig:event_end_notification_body'),0 FROM player WHERE status=10; DO memc_set('event_finished',1); SELECT sleep(1) INTO OUTFILE '/tmp/event_finished';END", $this->val); \Yii::$app->db->createCommand($Q)->execute(); $this->val = strtotime($this->val); } else { @@ -101,11 +101,9 @@ public function afterSave($insert, $changedAttributes) if ($this->id === 'stripe_webhookLocalEndpoint' && array_key_exists('val', $changedAttributes)) { $oldVal = $changedAttributes['val']; $newVal = $this->val; - if(($u=UrlRoute::findOne(['destination'=>'subscription/default/webhook']))!==NULL) - { - $u->updateAttributes(['source'=>$newVal]); + if (($u = UrlRoute::findOne(['destination' => 'subscription/default/webhook'])) !== NULL) { + $u->updateAttributes(['source' => $newVal]); } - } } diff --git a/backend/views/layouts/main.php b/backend/views/layouts/main.php index 707e5a4dd..75902eb66 100644 --- a/backend/views/layouts/main.php +++ b/backend/views/layouts/main.php @@ -12,12 +12,12 @@ $this->title = Yii::$app->sys->event_name . ' mUI: ' . $this->title; AppAsset::register($this); -$this->registerJsFile('@web/js/hljs/highlight.min.js',[ - 'depends' => [ - \yii\web\JqueryAsset::class - ] +$this->registerJsFile('@web/js/hljs/highlight.min.js', [ + 'depends' => [ + \yii\web\JqueryAsset::class + ] ]); -$this->registerCssFile('@web/js/hljs/styles/a11y-dark.min.css',['depends' => [\yii\web\JqueryAsset::class]]); +$this->registerCssFile('@web/js/hljs/styles/a11y-dark.min.css', ['depends' => [\yii\web\JqueryAsset::class]]); ?> beginPage() ?> @@ -31,6 +31,13 @@ registerCsrfMetaTags() ?> <?= Html::encode($this->title) ?> head() ?> +cache->memcache->get('sysconfig:event_start') !== false && Yii::$app->cache->memcache->get('sysconfig:event_end') !== false): ?> + + @@ -79,6 +86,9 @@

©

+
+

+

@@ -97,6 +107,5 @@ 'markdown-highlighter' ); ?> - endPage() ?> \ No newline at end of file diff --git a/backend/web/js/site.js b/backend/web/js/site.js index f35a68a21..8f2151908 100644 --- a/backend/web/js/site.js +++ b/backend/web/js/site.js @@ -3,3 +3,54 @@ $(function () { $("[data-toggle='tooltip']").tooltip(); $("[data-toggle='popover']").popover(); }); +$(document).ready(function () { + + var ticks = 0; + + var x = setInterval(function () { + ticks++; + + if (typeof countDownDate === 'undefined') + return; + + if (countDownDate === 0) { + clearInterval(x); + return; + } + + // Server-based time, tick-driven + var timeNow = countDownNow + (ticks * 1000); + + var distance = countDownDate - timeNow; + var element = document.getElementById("event_countdown"); + var msg = "The competition ends in: "; + + if (countDownStart > 0 && countDownStart > timeNow) { + distance = countDownStart - timeNow; + msg = "The competition starts in: "; + } + + if (distance < 0) { + clearInterval(x); + if (element) + element.innerHTML = 'The competition is finished'; + return; + } + + var days = Math.floor(distance / (1000 * 60 * 60 * 24)); + var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); + var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60)); + var seconds = Math.floor((distance % (1000 * 60)) / 1000); + + if (element) { + if (days > 0) + element.innerHTML = msg + days + "d " + hours + "h " + minutes + "m " + seconds + "s"; + else if (hours > 0) + element.innerHTML = msg + hours + "h " + minutes + "m " + seconds + "s"; + else if (minutes > 0) + element.innerHTML = msg + minutes + "m " + seconds + "s"; + else + element.innerHTML = msg + seconds + "s"; + } + }, 1000); +}); \ No newline at end of file diff --git a/contrib/event_shutdown.sh b/contrib/event_shutdown.sh new file mode 100644 index 000000000..057c3091d --- /dev/null +++ b/contrib/event_shutdown.sh @@ -0,0 +1,8 @@ +#!/bin/ksh +PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin:/usr/local/sbin:/usr/local/bin +rcctl stop openvpn findingsd heartbeatd inetd cron +supervisorctl stop all +backend vpn/killall +backend vpn/logoutall +backend target/destroy-instances +ifconfig tun0 down \ No newline at end of file diff --git a/contrib/findingsd-federated.sql b/contrib/findingsd-federated.sql index ac873b622..848499eae 100644 --- a/contrib/findingsd-federated.sql +++ b/contrib/findingsd-federated.sql @@ -116,6 +116,32 @@ CREATE TABLE `player_ssl` ( UNIQUE KEY `serial` (`serial`) ) ENGINE=FEDERATED DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci CONNECTION='mysql://{{db_user}}:{{db_pass}}@{{db_host}}:3306/{{db_name}}/player_ssl'; +DROP TABLE IF EXISTS `private_network`; +CREATE TABLE `private_network` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `player_id` int(11) unsigned DEFAULT NULL, + `name` varchar(255) DEFAULT NULL, + `team_accessible` tinyint(1) DEFAULT NULL, + `created_at` datetime DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx-private_network-player_id` (`player_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci CONNECTION='mysql://{{db_user}}:{{db_pass}}@{{db_host}}:3306/{{db_name}}/private_network'; + +DROP TABLE IF EXISTS `private_network_target`; +CREATE TABLE `private_network_target` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `private_network_id` int(11) DEFAULT NULL, + `target_id` int(11) NOT NULL, + `ip` int(11) unsigned DEFAULT NULL, + `state` smallint(6) unsigned DEFAULT 0, + `server_id` int(11) DEFAULT NULL, + `ipoctet` varchar(15) GENERATED ALWAYS AS (inet_ntoa(`ip`)) VIRTUAL, + PRIMARY KEY (`id`), + UNIQUE KEY `idx-unique-private_network_id-target_id` (`private_network_id`,`target_id`), + KEY `idx-private_network_target-private_network_id` (`private_network_id`), + KEY `idx-private_network_target-server_id` (`server_id`), + KEY `idx-private_network_target-target_id` (`target_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci CONNECTION='mysql://{{db_user}}:{{db_pass}}@{{db_host}}:3306/{{db_name}}/private_network_target'; DROP TABLE IF EXISTS `debuglogs`; CREATE TABLE debuglogs ( @@ -241,3 +267,17 @@ BEGIN END IF; END // + + +DROP EVENT IF EXISTS `event_shutdown` // +CREATE EVENT `event_shutdown` ON SCHEDULE EVERY 5 SECOND STARTS '2020-01-01 00:00:00' ON COMPLETION PRESERVE ENABLE DO +BEGIN + IF (select memc_server_count()<1) THEN + select memc_servers_set('{{db_host}}:{{memc_port|default(11211)}}') INTO @memc_server_set_status; + END IF; + + IF memc_get('event_finished') IS NOT NULL THEN + ALTER EVENT `event_shutdown` DISABLE; + SELECT 1 INTO OUTFILE '/tmp/event_finished'; + END IF; +END // diff --git a/contrib/openvpn_tun0.conf b/contrib/openvpn_tun0.conf index 6aea32ff1..516063fc8 100644 --- a/contrib/openvpn_tun0.conf +++ b/contrib/openvpn_tun0.conf @@ -12,7 +12,7 @@ client-config-dir /etc/openvpn/ccd writepid /var/run/openvpn.pid tls-auth /etc/openvpn/private/vpn-ta.key 0 replay-persist /etc/openvpn/replay-persist-file -status /var/log/openvpn/openvpn-status.log +status /var/log/openvpn/openvpn-status.log 5 log-append /var/log/openvpn/openvpn.log #chroot /var/openvpn/chrootjail crl-verify /etc/openvpn/crl.pem @@ -29,7 +29,7 @@ data-ciphers AES-128-GCM auth SHA256 compress verb 1 -keepalive 3 240 +keepalive 3 30 mute-replay-warnings script-security 2 diff --git a/contrib/sample-migrations/m000000_000001_system_settings.php b/contrib/sample-migrations/m000000_000001_system_settings.php index 5b76f0a90..ff7323611 100644 --- a/contrib/sample-migrations/m000000_000001_system_settings.php +++ b/contrib/sample-migrations/m000000_000001_system_settings.php @@ -31,6 +31,10 @@ class m000000_000001_system_settings extends Migration ['id' => "leaderboard_show_zero", 'val' => "0"], ['id' => "leaderboard_visible_after_event_end", 'val' => "1"], ['id' => "leaderboard_visible_before_event_start", 'val' => "0"], + ['id' => "country_rankings", 'val' => "0"], + ['id' => "player_point_rankings", 'val' => "0"], + ['id' => "player_monthly_rankings", 'val' => "0"], + ['id' => 'frontpage_scenario', 'val' => 'Welcome to our lovely event... Edit from backend Content => Frontpage Scenario'], ['id' => "event_end_notification_title", 'val' => "🎉 Our awesome echoCTF finished 🎉"], ['id' => "event_end_notification_body", 'val' => "The awesome echoCTF is over 🎉🎉🎉 Congratulations to you and your team 👏👏👏 Thank you for participating!!!"], @@ -59,23 +63,38 @@ class m000000_000001_system_settings extends Migration ['id' => "team_manage_members", 'val' => "1"], ['id' => "team_required", 'val' => "1"], ['id' => 'team_visible_instances', 'val' => "1"], + ['id' => 'team_only_leaderboards', 'val' => "1"], + ['id' => 'team_encrypted_claims_allowed', 'val' => "1"], + /** * Player settings */ ['id' => "approved_avatar", 'val' => "1"], ['id' => "player_profile", 'val' => "1"], ['id' => "profile_visibility", 'val' => "public"], - ['id' => "require_activation", 'val' => "0"], - ['id' => 'player_require_identification', 'val' => "0"], + ['id' => "require_activation", 'val' => "1"], + ['id' => 'player_require_identification', 'val' => "1"], ['id' => 'all_players_vip', 'val' => "1"], - ['id' => 'player_require_approval', 'val' => "0"], + ['id' => 'player_require_approval', 'val' => "1"], ['id' => 'profile_discord', 'val' => "1"], ['id' => 'profile_echoctf', 'val' => "1"], ['id' => 'profile_github', 'val' => "1"], ['id' => 'profile_settings_fields', 'val' => 'avatar,bio,country,discord,echoctf,email,fullname,github,pending_progress,twitter,username,visibility'], + ['id' => 'avatar_robohash_set', 'val' => 'set3'], + /** * Configuration settings */ + ['id' => 'target_guest_view_deny', 'val' => '1'], + ['id' => 'disable_ondemand_operations', 'val' => '1'], + ['id' => 'module_smartcity_disabled', 'val' => '1'], + ['id' => 'module_speedprogramming_enabled', 'val' => '0'], + ['id' => 'dashboard_news_total_pages', 'val' => '10'], + ['id' => 'dashboard_news_records_per_page', 'val' => '3'], + ['id' => 'force_https_urls', 'val' => '1'], + ['id' => 'subscriptions_menu_show', 'val' => '0'], + ['id' => 'log_failed_claims', 'val' => '1'], + ['id' => 'academic_grouping', 'val' => '0'], ['id' => "challenge_home", 'val' => "uploads/"], ['id' => "dashboard_is_home", 'val' => "1"], @@ -126,8 +145,11 @@ class m000000_000001_system_settings extends Migration */ public function safeUp() { - foreach ($this->news as $entry) + foreach ($this->news as $entry) { + $entry['created_at']=new \yii\db\Expression('NOW()'); + $entry['updated_at']=new \yii\db\Expression('NOW()'); $this->upsert('news', $entry, true); + } // delete not needed url routes foreach ($this->delete_url_routes as $route) { diff --git a/contrib/watchdog-action.py b/contrib/watchdog-action.py new file mode 100644 index 000000000..d5eaa9cf5 --- /dev/null +++ b/contrib/watchdog-action.py @@ -0,0 +1,33 @@ +#!/usr/local/bin/python3 +# +# pip install watchdog +import argparse +import os +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler + +# CLI arguments +parser = argparse.ArgumentParser() +parser.add_argument("--file_path", required=True, help="Full path to the file to monitor") +parser.add_argument("--action", required=True, help="Full path to the file we will execute") +args = parser.parse_args() + +FULL_PATH = args.file_path +FOLDER = os.path.dirname(FULL_PATH) +TARGET_FILE = os.path.basename(FULL_PATH) +ACTION = args.action + +class Handler(FileSystemEventHandler): + def __init__(self, observer): + self.observer = observer + + def on_created(self, event): + if not event.is_directory and event.src_path == FULL_PATH: + os.system(ACTION) + self.observer.stop() + +observer = Observer() +handler = Handler(observer) +observer.schedule(handler, FOLDER, recursive=False) +observer.start() +observer.join() diff --git a/contrib/watchdoger.py b/contrib/watchdoger.py new file mode 100644 index 000000000..38651de7a --- /dev/null +++ b/contrib/watchdoger.py @@ -0,0 +1,46 @@ +#!/usr/local/bin/python3 +# +# pip install watchdog requests +import argparse +import os +import requests +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler + +# CLI arguments +parser = argparse.ArgumentParser() +parser.add_argument("--file_path", required=True, help="Full path to the file to monitor") +parser.add_argument("--url", required=True, help="HTTP endpoint URL to POST to") +parser.add_argument("--token", required=True, help="Bearer token for authorization") +args = parser.parse_args() + +FULL_PATH = args.file_path +FOLDER = os.path.dirname(FULL_PATH) +TARGET_FILE = os.path.basename(FULL_PATH) +URL = args.url +BEARER_TOKEN = args.token + +class Handler(FileSystemEventHandler): + def __init__(self, observer): + self.observer = observer + + def on_created(self, event): + if not event.is_directory and event.src_path == FULL_PATH: + response = requests.post( + URL, + headers={ + "Authorization": f"Bearer {BEARER_TOKEN}", + "Content-Type": "application/json" + }, + json={ + "event": "apiNotifications" + } + ) + print(f"Posted {event.src_path}, status: {response.status_code}") + self.observer.stop() # exit after sending + +observer = Observer() +handler = Handler(observer) +observer.schedule(handler, FOLDER, recursive=False) +observer.start() +observer.join() diff --git a/docs/Websockets.md b/docs/Websockets.md new file mode 100644 index 000000000..3b46b4756 --- /dev/null +++ b/docs/Websockets.md @@ -0,0 +1,59 @@ +# Websockets service + +echoCTF.RED provides player updates to the live players through the use of [ws-server](https://github.com/echoCTF/ws-server). + +The services that want to communicate an update to the current live players submit their events through the HTTP service of ws-server. + +The system can send messages to a specific player or all connected players through the `/publish` and `/broadcast` endpoints respectively. + +Currently the following events are implemented: + +* `notification`: Sends a direct notification, Alert or Sweetalerts. +* `apiNotifications`: Tell the clients to perform an update of their in-page notifications. +* `target`: Update the target card if currently visible + +## Examples + +* Notify all users to perform an `apiNotifications()` js call. Effectively fetch the latest notifications through ajax. + +```shell +curl -X POST "http://localhost:8888/broadcast" \ + -H "Authorization: Bearer YOURTOKEN" \ + -H "Content-Type: application/json" \ + -d '{ "event": "apiNotifications" }' +``` + +* Send a SweetAlert (`"type": "swap:info"`) notification to player with id `1` + +```shell +curl -X POST "http://localhost:8888/publish" \ + -H "Authorization: Bearer server123token" \ + -H "Content-Type: application/json" \ + -d '{ + "player_id": "1", + "event": "notification", + "payload": + { + "title": "This is a notification", + "body": "This is the notification body", + "type": "swal:info" + } + }' +``` + +Note: Removing the `swal:` prefix from `type` sends a normal bootstrap alert notification. + +* Send an update for to player id `1` for updates on target id `2` + +```shell +curl -X POST "http://localhost:8888/publish" \ + -H "Authorization: Bearer server123token" \ + -H "Content-Type: application/json" \ + -d '{ + "player_id": "1", + "event": "target", + "payload": { "id": "2" } + }' +``` + +This will execute the js code `targetUpdates(2)`. diff --git a/frontend/commands/TesterController.php b/frontend/commands/TesterController.php deleted file mode 120000 index b00eb05cc..000000000 --- a/frontend/commands/TesterController.php +++ /dev/null @@ -1 +0,0 @@ -../../backend/commands/TesterController.php \ No newline at end of file diff --git a/frontend/commands/TesterController.php b/frontend/commands/TesterController.php new file mode 100644 index 000000000..4dc1dbdf3 --- /dev/null +++ b/frontend/commands/TesterController.php @@ -0,0 +1,88 @@ +stdout("*** TESTER COMMAND ***\n"); + + echo Table::widget([ + 'headers' => ['Action', 'Usage', 'Description'], + 'rows' => [ + ['Action' => 'tester/mail', 'Usage' => 'tester/mail email@example.com', 'Description' => 'Send a test mail with the current settings'], + ['Action' => 'tester/ws-notify', 'Usage' => 'tester/ws-notify player', 'Description' => 'Send a test websocket notification to the given player by id'], + ], + ]); + } + + /** + * Test the mailer configuration by sending a test email. + * + * Usage: + * backend tester/mail test@example.com + * + * @param string $to Recipient email + */ + public function actionMail($to) + { + $mailer = Yii::$app->mailer; + try { + + $this->stdout("*** SETTINGS *** \n"); + if (\Yii::$app->sys->mail_useFileTransport) { + $this->stdout("mail_useFileTransport: Yes\n"); + $this->stdout("mails folder: " . @\Yii::getAlias('@app/runtime/mail/') . "\n"); + } + if (\Yii::$app->sys->dsn) $this->stdout("dsn: " . \Yii::$app->sys->dsn . "\n"); + if (\Yii::$app->sys->mail_from) $this->stdout("mail_from: " . \Yii::$app->sys->mail_from . "\n"); + if (\Yii::$app->sys->mail_fromName) $this->stdout("mail_fromName: " . \Yii::$app->sys->mail_fromName . "\n"); + if (\Yii::$app->sys->mail_host) $this->stdout("mail_host: " . \Yii::$app->sys->mail_host . "\n"); + if (\Yii::$app->sys->mail_port) $this->stdout("mail_port: " . \Yii::$app->sys->mail_port . "\n"); + if (\Yii::$app->sys->mail_username) $this->stdout("mail_username: " . \Yii::$app->sys->mail_username . "\n"); + if (\Yii::$app->sys->mail_password) $this->stdout("mail_password: **USED BUT HIDDEN**\n"); + + $result = $mailer->compose() + ->setFrom([\Yii::$app->sys->mail_from => \Yii::$app->sys->mail_fromName]) + ->setTo($to) + ->setSubject('echoCTF Installation Mail Test') + ->setTextBody("This is a test email sent at " . date('Y-m-d H:i:s')) + ->send(); + if ($result) { + $this->stdout("✅ Test email successfully sent to {$to}\n"); + } else { + $this->stderr("❌ Failed to send test email to {$to}\n"); + } + } catch (\Symfony\Component\Mailer\Exception\TransportExceptionInterface $e) { + $this->stderr("❌ Transport error: " . $e->getMessage() . "\n"); + } catch (\Throwable $e) { + $this->stderr("❌ Error: " . $e->getMessage() . "\n"); + } + } + + public function actionWsNotify($player) + { + $player=\app\models\Player::findOne($player); + $type = "info"; + $title="title"; + $body="body"; + $cc = true; + $archive = true; + $apiOnly = false; + $player->notify($type, $title, $body, $cc, $archive, $apiOnly); + } + +} diff --git a/frontend/components/Img.php b/frontend/components/Img.php index f4371ad72..75126b2e1 100644 --- a/frontend/components/Img.php +++ b/frontend/components/Img.php @@ -55,12 +55,22 @@ public static function profile($profile) imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"root@%s:/# ./userinfo --profile %d"),\Yii::$app->sys->offense_domain,$profile->id),$textcolor); imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"username.....: %s"),$profile->owner->username),$greencolor); imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"joined.......: %s"),date("d.m.Y", strtotime($profile->owner->created))),$greencolor); - imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"points.......: %s"),number_format($profile->owner->playerScore->points)),$greencolor); - imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"rank.........: %s"),$profile->owner->playerScore->points == 0 ? "-":$profile->rank->ordinalPlace),$greencolor); - imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"level........: %d / %s"),$profile->experience->id, $profile->experience->name),$greencolor); - imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"flags........: %d"), $profile->totalTreasures),$greencolor); - imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"challenges...: %d / %d first"),$profile->challengesSolverCount, $profile->firstChallengeSolversCount),$greencolor); - imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"headshots....: %d / %d first"),$profile->headshotsCount, $profile->firstHeadshotsCount),$greencolor); + if (\Yii::$app->sys->team_only_leaderboards !== true) + { + imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"points.......: %s"),number_format($profile->owner->playerScore->points)),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"rank.........: %s"),$profile->owner->playerScore->points == 0 ? "-":$profile->rank->ordinalPlace),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"level........: %d / %s"),$profile->experience->id, $profile->experience->name),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"flags........: %d"), $profile->totalTreasures),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"challenges...: %d / %d first"),$profile->challengesSolverCount, $profile->firstChallengeSolversCount),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, sprintf(\Yii::t('app',"headshots....: %d / %d first"),$profile->headshotsCount, $profile->firstHeadshotsCount),$greencolor); + } + else if($profile->owner->teamPlayer) + { + imagestring($image, 6, 200, $lineheight*$i++, \Yii::t('app',"team.........: {team}",['team'=>$profile->owner->team->name]),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, \Yii::t('app',"team rank....: {rank}",['rank'=>($profile->owner->team->rank !== null ? $profile->owner->team->rank->ordinalPlace : 'empty')]),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, \Yii::t('app',"team points..: {points,plural,=0{0 pts} =1{# pts} other{# pts}}",['points'=>($profile->owner->team->score !== null ? $profile->owner->team->score->points : 0)]),$greencolor); + imagestring($image, 6, 200, $lineheight*$i++, \Yii::t('app',"contributed..: {points,plural,=0{0 pts} =1{# pts} other{# pts}}",['points'=>($profile->owner->teamStreamPoints->points ?? 0)]),$greencolor); + } imagedestroy($avatar); imagedestroy($cover); imagedestroy($src); diff --git a/frontend/config/console.php b/frontend/config/console.php index 4b146d6c7..61ad81f0e 100644 --- a/frontend/config/console.php +++ b/frontend/config/console.php @@ -1,54 +1,60 @@ 'basic-console', -// 'language' => 'el-GR', - 'sourceLanguage' => 'en-US', - 'basePath' => dirname(__DIR__), - 'bootstrap' => ['log'], - 'controllerNamespace' => 'app\commands', - 'aliases' => [ - '@bower' => '@vendor/bower-asset', - '@npm' => '@vendor/npm-asset', - '@tests' => '@app/tests', +$config = [ + 'id' => 'basic-console', + // 'language' => 'el-GR', + 'sourceLanguage' => 'en-US', + 'basePath' => dirname(__DIR__), + 'bootstrap' => ['log'], + 'controllerNamespace' => 'app\commands', + 'aliases' => [ + '@bower' => '@vendor/bower-asset', + '@npm' => '@vendor/npm-asset', + '@tests' => '@app/tests', + ], + 'modules' => [ + 'team' => [ + 'class' => 'app\modules\team\Module', ], - 'components' => [ - 'i18n' => [ - 'translations' => [ - 'yii' => [ - 'class' => 'yii\i18n\PhpMessageSource', - ], - 'app*' => [ - 'class' => 'yii\i18n\PhpMessageSource', - 'basePath' => '@app/messages', - 'sourceLanguage' => 'en-US', - 'fileMap' => [ - 'app' => 'app.php', - 'app/error' => 'error.php', - ], - ], - ], + ], + + 'components' => [ + 'i18n' => [ + 'translations' => [ + 'yii' => [ + 'class' => 'yii\i18n\PhpMessageSource', ], - 'sys'=> [ - 'class' => 'app\components\Sysconfig', + 'app*' => [ + 'class' => 'yii\i18n\PhpMessageSource', + 'basePath' => '@app/messages', + 'sourceLanguage' => 'en-US', + 'fileMap' => [ + 'app' => 'app.php', + 'app/error' => 'error.php', + ], ], - 'cache' => $cache, - 'log' => [ - 'targets' => [ - [ - 'class' => 'yii\log\FileTarget', - 'levels' => ['error', 'warning'], - ], - ], + ], + ], + 'sys' => [ + 'class' => 'app\components\Sysconfig', + ], + 'cache' => $cache, + 'log' => [ + 'targets' => [ + [ + 'class' => 'yii\log\FileTarget', + 'levels' => ['error', 'warning'], ], - 'db' => $db, + ], ], - 'params' => $params, - /* + 'db' => $db, + ], + 'params' => $params, + /* 'controllerMap' => [ 'fixture' => [ // Fixture generation command line. 'class' => 'yii\faker\FixtureController', @@ -57,13 +63,12 @@ */ ]; -if(YII_ENV_DEV) -{ - // configuration adjustments for 'dev' environment - $config['bootstrap'][]='gii'; - $config['modules']['gii']=[ - 'class' => 'yii\gii\Module', - ]; +if (YII_ENV_DEV) { + // configuration adjustments for 'dev' environment + $config['bootstrap'][] = 'gii'; + $config['modules']['gii'] = [ + 'class' => 'yii\gii\Module', + ]; } return $config; diff --git a/frontend/config/web.php b/frontend/config/web.php index 1adc34bee..05f104a1e 100644 --- a/frontend/config/web.php +++ b/frontend/config/web.php @@ -173,7 +173,7 @@ 'name' => 'red', 'timeout' => 3600 * 12, 'cookieParams' => [ - 'secure' => true, + 'secure' => YII_ENV_DEV ? false : true, 'sameSite' => 'Lax', 'lifetime' => 3600 * 12, 'httpOnly' => true @@ -185,7 +185,7 @@ 'enableAutoLogin' => true, 'identityCookie' => [ 'name' => '_identity-red', - 'secure' => true, + 'secure' => YII_ENV_DEV ? false : true, 'httpOnly' => true, 'sameSite'=>'Strict' ], diff --git a/frontend/models/Player.php b/frontend/models/Player.php index 0f4720f07..669cc5b3a 100644 --- a/frontend/models/Player.php +++ b/frontend/models/Player.php @@ -408,21 +408,25 @@ public function notify($type = "info", $title, $body, $cc = true, $archive = tru try { $publisher = new \app\services\ServerPublisher(Yii::$app->params['serverPublisher']); $publisher->publish($this->id, 'notification', ['type' => $type, 'title' => $title, 'body' => $body]); - } catch(\Throwable $e) { + } catch (\Throwable $e) { // on publishing error make sure we store the noticication as pending - $cc=true; - $archive=false; + $cc = true; + $archive = false; Yii::error($e->getMessage()); } if ($cc === true) { $n = new \app\models\Notification; $n->player_id = $this->id; - $n->archived = $archive; + $n->archived = intval($archive); $n->category = $type; $n->title = $title; $n->body = $body; - return $n->save(); + if (!$n->save()) { + Yii::error($n->getErrorSummary(true)); + return false; + } + return true; } return true; } diff --git a/frontend/modules/target/actions/SpawnRestAction.php b/frontend/modules/target/actions/SpawnRestAction.php index e7b0adb6d..916243e49 100644 --- a/frontend/modules/target/actions/SpawnRestAction.php +++ b/frontend/modules/target/actions/SpawnRestAction.php @@ -59,7 +59,7 @@ public function run($id,$team=false) $ti=new TargetInstance; $ti->player_id=Yii::$app->user->id; $ti->target_id=$id; - // pick the least used server currently + $ti->team_allowed=intval(\Yii::$app->sys->team_visible_instances); if(\Yii::$app->user->identity->subscription !== null && \Yii::$app->user->identity->subscription->active > 0 && \Yii::$app->user->identity->subscription->product !== null) { $metadata = json_decode(\Yii::$app->user->identity->subscription->product->metadata); @@ -67,6 +67,8 @@ public function run($id,$team=false) $ti->team_allowed=($team===false ? 0 : 1); } } + + // pick the least used server currently $ti->server_id=intval(Yii::$app->db->createCommand('select id from server t1 left join target_instance t2 on t1.id=t2.server_id group by t1.id order by count(t2.server_id) limit 1')->queryScalar()); if($ti->save()!==false) Yii::$app->session->setFlash('success', sprintf(\Yii::t('app','Spawning new instance for [%s]. You will receive a notification when the instance is up.'), $ti->target->name)); diff --git a/frontend/modules/target/models/Treasure.php b/frontend/modules/target/models/Treasure.php index f58bf6ccd..ccc44983b 100644 --- a/frontend/modules/target/models/Treasure.php +++ b/frontend/modules/target/models/Treasure.php @@ -146,7 +146,7 @@ public function getTarget() */ public function getLocationRedacted() { - return str_replace($this->code,"*REDACTED*",$this->location); + return str_replace($this->code,"*REDACTED*",$this->solution); } public static function find() diff --git a/frontend/modules/team/controllers/DefaultController.php b/frontend/modules/team/controllers/DefaultController.php index d64f9b9b4..4ca6c0a39 100644 --- a/frontend/modules/team/controllers/DefaultController.php +++ b/frontend/modules/team/controllers/DefaultController.php @@ -221,7 +221,6 @@ public function actionView($token) 'pageSize' => 10, ] ]); - return $this->render('view', [ 'team' => $model, 'teamInstanceProvider' => $teamInstanceProvider, @@ -264,8 +263,23 @@ public function actionMine() ] ]); + $teamNetworks = \app\modules\network\models\PrivateNetwork::find()->forTeam(\Yii::$app->user->identity->team->id); + $teamNetworksProvider = new ActiveDataProvider([ + 'query' => $teamNetworks, + 'pagination' => [ + 'pageSizeParam' => 'networks-perpage', + 'pageParam' => 'networks-page', + 'pageSize' => 5, + ], + 'sort' => ['defaultOrder' => ['name' => SORT_ASC]], + ]); + + $subQuery = TeamStream::find() + ->select('stream_id') + ->where(['team_id' => \Yii::$app->user->identity->team->id]); + $stream = \app\models\Stream::find()->select('stream.*,TS_AGO(ts) as ts_ago') - ->where(['stream.player_id' => $teamPlayers]) + ->where(['id' => $subQuery]) ->orderBy(['ts' => SORT_DESC, 'id' => SORT_DESC]); $streamProvider = new ActiveDataProvider([ 'query' => $stream, @@ -314,7 +328,9 @@ public function actionMine() 'teamTargetsProvider' => $targetProgressProvider, 'headshotsProvider' => $headshotsProvider, 'solverProvider' => $solverProvider, - 'team' => Yii::$app->user->identity->team + 'team' => Yii::$app->user->identity->team, + 'networksProvider' => $teamNetworksProvider, + ]); } /** diff --git a/frontend/modules/team/models/Team.php b/frontend/modules/team/models/Team.php index b84435fe7..258454e7b 100644 --- a/frontend/modules/team/models/Team.php +++ b/frontend/modules/team/models/Team.php @@ -25,7 +25,8 @@ * @property Player $owner * @property TeamPlayer[] $teamPlayers * @property Player[] $players - */ + * @property TeamInvite $inviteOrCreate +*/ class Team extends \yii\db\ActiveRecord { public $uploadedAvatar; @@ -142,7 +143,7 @@ public function getRank() */ public function getTeamPlayers() { - return $this->hasMany(TeamPlayer::class, ['team_id' => 'id'])->orderBy(['approved'=>SORT_DESC,'ts'=>SORT_ASC]); + return $this->hasMany(TeamPlayer::class, ['team_id' => 'id'])->orderBy(['approved' => SORT_DESC, 'ts' => SORT_ASC]); } /** @@ -161,6 +162,28 @@ public function getInvite() return $this->hasOne(TeamInvite::class, ['team_id' => 'id']); } + /** + * Returns the related TeamInvite model. + * + * If the invite does not exist yet, it will be created, saved, + * and populated into the `invite` relation. + * + * @return TeamInvite the existing or newly created invite model + * @throws \RuntimeException if the invite cannot be created + */ + public function getInviteOrCreate() + { + if ($this->invite === null) { + $invite = new TeamInvite(['team_id'=>$this->id,'token'=>Yii::$app->security->generateRandomString(8)]); + if (!$invite->save()) { + throw new \RuntimeException('Failed to create TeamInvite'); + } + $this->populateRelation('invite', $invite); + } + + return $this->invite; + } + public function getValidLogo() { if ($this->logo === null || trim($this->logo) === '') @@ -286,21 +309,19 @@ public function getAcademicWord() /** * Generate a new invite url */ - public function generate_invite(){ - if($this->invite) { - $this->invite->token=Yii::$app->security->generateRandomString(8); - if(!$this->invite->save()) - { - throw new UserException(Yii::t('app','Failed to save invite. [{error}]',['error'=>implode(" ",$this->invite->getErrors())])); + public function generate_invite() + { + if ($this->invite) { + $this->invite->token = Yii::$app->security->generateRandomString(8); + if (!$this->invite->save()) { + throw new UserException(Yii::t('app', 'Failed to save invite. [{error}]', ['error' => implode(" ", $this->invite->getErrors())])); } - } - else { - $ti=new TeamInvite; - $ti->team_id=$this->id; - $ti->token=Yii::$app->security->generateRandomString(8); - if(!$ti->save()) - { - throw new UserException(Yii::t('app','Failed to save invite. [{error}]',['error'=>implode(" ",$ti->getErrors())])); + } else { + $ti = new TeamInvite; + $ti->team_id = $this->id; + $ti->token = Yii::$app->security->generateRandomString(8); + if (!$ti->save()) { + throw new UserException(Yii::t('app', 'Failed to save invite. [{error}]', ['error' => implode(" ", $ti->getErrors())])); } } } diff --git a/frontend/themes/material/layouts/main.php b/frontend/themes/material/layouts/main.php index a66aacfcc..80d5972ae 100644 --- a/frontend/themes/material/layouts/main.php +++ b/frontend/themes/material/layouts/main.php @@ -50,6 +50,7 @@ sys->event_start!==false && Yii::$app->sys->event_end!==false):?> @@ -71,7 +72,7 @@
sys->{"footer_logos"}?>
diff --git a/frontend/themes/material/modules/target/views/default/_target_card.php b/frontend/themes/material/modules/target/views/default/_target_card.php index 284e2b9c8..53779fdcd 100644 --- a/frontend/themes/material/modules/target/views/default/_target_card.php +++ b/frontend/themes/material/modules/target/views/default/_target_card.php @@ -21,10 +21,10 @@ else $display_ip=Html::a($target_ip,$target_ip,["class"=>'copy-to-clipboard text-danger text-bold','swal-data'=>"Copied to clipboard",'data-toggle'=>'tooltip','title'=>\Yii::t('app',"The IP of your private instance. Click to copy IP to clipboard.")]); } -if($target_ip=='0.0.0.0') -{ - $this->registerJs("targetUpdates({$target->id});", \yii\web\View::POS_READY); -} +//if($target_ip=='0.0.0.0') +//{ +// $this->registerJs("targetUpdates({$target->id});", \yii\web\View::POS_READY); +//} $subtitleARR=[$target->category,ucfirst($target->getDifficultyText($target->average_rating)),boolval($target->rootable) ? "Rootable" : "Non rootable",$target->timer===false ? null:'Timed']; $subtitle=implode(", ",array_filter($subtitleARR)); Card::begin([ @@ -34,7 +34,7 @@ 'icon'=>sprintf('', $target->logo), 'color'=>'target', 'subtitle'=>$subtitle, - 'title'=>sprintf('%s / %s', $target->name, $display_ip), + 'title'=>sprintf('%s / %s', $target->name, $target->id, $display_ip), 'footer'=>sprintf('
%s
%s', $target->purpose, TargetCardActions::widget(['model'=>$target,'identity'=>$identity]) ), ]); echo "

", $target->total_treasures, ": Flag".($target->total_treasures > 1 ? 's' : '')." "; diff --git a/frontend/themes/material/modules/target/views/default/_target_metadata.php b/frontend/themes/material/modules/target/views/default/_target_metadata.php index f96d082ec..2ee835d76 100644 --- a/frontend/themes/material/modules/target/views/default/_target_metadata.php +++ b/frontend/themes/material/modules/target/views/default/_target_metadata.php @@ -1,11 +1,13 @@ user->isGuest && $metadata):?> + formatter->divID; ?> user->identity->isAdmin):?> - scenario)):?>: scenario,'gfm')?>
- instructions)):?>: instructions,'gfm')?>
- solution)):?>: solution,'gfm')?>
+ scenario)):?>: formatter->divID = 'markdown-scenario'; echo \Yii::$app->formatter->asMarkdown($metadata->scenario)?> + instructions)):?>: formatter->divID = 'markdown-instructions'; echo \Yii::$app->formatter->asMarkdown($metadata->instructions)?> + solution)):?>: formatter->divID = 'markdown-solution'; echo \Yii::$app->formatter->asMarkdown($metadata->solution)?> - pre_credits)):?>: pre_credits,'gfm')?>
- pre_exploitation)):?>: pre_exploitation,'gfm')?>
- player_id===Yii::$app->user->id && $target->progress==100) || Yii::$app->user->identity->isAdmin) && !empty($metadata->post_exploitation)):?>: post_exploitation,'gfm')?>
- player_id===Yii::$app->user->id && $target->progress==100) || Yii::$app->user->identity->isAdmin) && !empty($metadata->post_credits)):?>: post_credits,'gfm')?>
+ pre_credits)):?>: formatter->divID = 'markdown-pre-credits'; echo \Yii::$app->formatter->asMarkdown($metadata->pre_credits)?> + pre_exploitation)):?>: formatter->divID = 'markdown-pre-exploitation'; echo \Yii::$app->formatter->asMarkdown($metadata->pre_exploitation)?> + player_id===Yii::$app->user->id && $target->progress==100) || Yii::$app->user->identity->isAdmin) && !empty($metadata->post_exploitation)):?>: formatter->divID = 'markdown-post-exploitation'; echo \Yii::$app->formatter->asMarkdown($metadata->post_exploitation)?> + player_id===Yii::$app->user->id && $target->progress==100) || Yii::$app->user->identity->isAdmin) && !empty($metadata->post_credits)):?>: formatter->divID = 'markdown-post-credits'; echo \Yii::$app->formatter->asMarkdown($metadata->post_credits)?> + formatter->divID=$oldId; ?> diff --git a/frontend/themes/material/modules/target/views/default/_versus.php b/frontend/themes/material/modules/target/views/default/_versus.php index 83185c2f3..cc5e36332 100644 --- a/frontend/themes/material/modules/target/views/default/_versus.php +++ b/frontend/themes/material/modules/target/views/default/_versus.php @@ -29,6 +29,18 @@ $this->registerMetaTag(['name' => 'game:points', 'content' => '0']); $this->registerMetaTag(['name' => 'article:published_time', 'content' => $headshot->created_at]); } +$this->registerJsFile('@web/js/showdown.min.js',[ + 'depends' => [ + \yii\web\JqueryAsset::class + ] +]); +$this->registerJsFile('@web/assets/hljs/highlight.min.js',[ + 'depends' => [ + \yii\web\JqueryAsset::class + ] +]); +$this->registerCssFile('@web/assets/hljs/styles/a11y-dark.min.css',['depends' => ['app\assets\MaterialAsset']]); + ?>

@@ -167,4 +179,11 @@ render('_target_writeups', ['writeups' => $target->writeups, 'active' => false, 'writeups_activated' => (PTH::findOne(['player_id' => Yii::$app->user->id, 'target_id' => $target->id]) !== null || Headshot::findOne(['player_id' => Yii::$app->user->id, 'target_id' => $target->id]) !== null)]); ?>
-
\ No newline at end of file + +registerJs( + 'hljs.highlightAll();', + $this::POS_READY, + 'markdown-highlighter' +); +?> diff --git a/frontend/themes/material/modules/team/views/default/_team_card.php b/frontend/themes/material/modules/team/views/default/_team_card.php index 08939f0de..8b540334c 100644 --- a/frontend/themes/material/modules/team/views/default/_team_card.php +++ b/frontend/themes/material/modules/team/views/default/_team_card.php @@ -89,9 +89,9 @@
user->identity->isAdmin || (Yii::$app->user->identity->team && !$model->inviteonly || ($invite === false && $listing === true))) : ?> $model->token], ['class' => 'btn block text-dark text-bold orbitron' . (!$model->inviteonly ? ' btn-info' : ' btn-warning')]) ?> - getTeamPlayers()->count()) < Yii::$app->sys->members_per_team && !Yii::$app->user->identity->team && !$model->locked && $model->invite) : ?> - $model->invite->token], ['class' => 'btn block btn-primary text-dark text-bold orbitron', 'data-method' => 'POST', 'data' => ['confirm' => 'You are about to join this team. Your membership will have to be confirmed by the team captain.', 'method' => 'POST']]) ?> + getTeamPlayers()->count()) < Yii::$app->sys->members_per_team && !Yii::$app->user->identity->team && !$model->locked && $model->inviteOrCreate) : ?> + $model->inviteOrCreate->token], ['class' => 'btn block btn-primary text-dark text-bold orbitron', 'data-method' => 'POST', 'data' => ['confirm' => 'You are about to join this team. Your membership will have to be confirmed by the team captain.', 'method' => 'POST']]) ?>

- \ No newline at end of file + diff --git a/frontend/themes/material/modules/team/views/default/view.php b/frontend/themes/material/modules/team/views/default/view.php index 23e71ed62..4a34bd3be 100644 --- a/frontend/themes/material/modules/team/views/default/view.php +++ b/frontend/themes/material/modules/team/views/default/view.php @@ -19,10 +19,10 @@

[name) ?>]

getTeamPlayers()->count() < Yii::$app->sys->members_per_team): ?>

- owner_id === Yii::$app->user->id || ($team->invite && !$team->inviteonly)): ?> + owner_id === Yii::$app->user->id || ($team->inviteOrCreate && !$team->inviteonly)): ?> owner_id === Yii::$app->user->id) $class .= ' copy-to-clipboard'; ?> - $team->invite->token], 'https'), Url::to(['/team/default/invite', 'token' => $team->invite->token], 'https'), ['class' => $class, 'swal-data' => 'Copied to clipboard!']); ?> + $team->inviteOrCreate->token], 'https'), Url::to(['/team/default/invite', 'token' => $team->inviteOrCreate->token], 'https'), ['class' => $class, 'swal-data' => 'Copied to clipboard!']); ?> recruitment) ?> @@ -179,8 +179,8 @@

render('../_profile_tabs',['profile'=>$profile,'game'=>$game,'headshots'=>$headshots]);?> +sys->team_only_leaderboards!==true):?>
+ diff --git a/frontend/web/429.html b/frontend/web/429.html index edd87e5a2..114f00e97 100644 --- a/frontend/web/429.html +++ b/frontend/web/429.html @@ -2,7 +2,6 @@ - echoCTF: Error 429